vram: document MemArbiter, fix round-robin ordering bug
This commit is contained in:
parent
f31f64f5a2
commit
191cd1bfa2
|
@ -8,25 +8,26 @@ export MemArbiterServer(..);
|
||||||
export MemArbiterClient(..);
|
export MemArbiterClient(..);
|
||||||
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
|
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
|
||||||
|
|
||||||
|
// A MemArbiterOp is an operation that a client is seeking permission
|
||||||
|
// to perform.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Bool write;
|
Bool write;
|
||||||
addr addr;
|
addr addr;
|
||||||
} MemArbiterOp#(type addr) deriving (Bits, Eq, FShow);
|
} MemArbiterOp#(type addr) deriving (Bits, Eq, FShow);
|
||||||
|
|
||||||
// A MemArbiterServer receives requests for memory access and emits
|
// A MemArbiterServer receives requests and emits grants.
|
||||||
// grants.
|
|
||||||
interface MemArbiterServer#(type addr);
|
interface MemArbiterServer#(type addr);
|
||||||
method Action request(MemArbiterOp#(addr) req);
|
method Action request(MemArbiterOp#(addr) req);
|
||||||
method Bool grant();
|
method Bool grant();
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
// A MemArbiterClient emits requests for memory access and emits
|
// A MemArbiterClient emits requests and receives grants.
|
||||||
// grants.
|
|
||||||
interface MemArbiterClient#(type addr);
|
interface MemArbiterClient#(type addr);
|
||||||
method Maybe#(MemArbiterOp#(addr)) request();
|
method Maybe#(MemArbiterOp#(addr)) request();
|
||||||
method Action grant();
|
method Action grant();
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
|
// Arbiter clients and servers can be connected in the obvious way.
|
||||||
instance Connectable#(MemArbiterClient#(addr), MemArbiterServer#(addr));
|
instance Connectable#(MemArbiterClient#(addr), MemArbiterServer#(addr));
|
||||||
module mkConnection(MemArbiterClient#(addr) client, MemArbiterServer#(addr) server, Empty ifc);
|
module mkConnection(MemArbiterClient#(addr) client, MemArbiterServer#(addr) server, Empty ifc);
|
||||||
rule send_request (client.request matches tagged Valid .req);
|
rule send_request (client.request matches tagged Valid .req);
|
||||||
|
@ -39,12 +40,42 @@ instance Connectable#(MemArbiterClient#(addr), MemArbiterServer#(addr));
|
||||||
endmodule
|
endmodule
|
||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
|
// A MemArbiter manages concurrent access to a memory port.
|
||||||
interface MemArbiter#(numeric type num_clients, type addr);
|
interface MemArbiter#(numeric type num_clients, type addr);
|
||||||
|
// ports allow clients to request memory access.
|
||||||
interface Vector#(num_clients, MemArbiterServer#(addr)) ports;
|
interface Vector#(num_clients, MemArbiterServer#(addr)) ports;
|
||||||
|
|
||||||
|
// The following methods are to support arbiter chaining.
|
||||||
|
//
|
||||||
|
// Suppose you're arbitrating access to a dual-port
|
||||||
|
// memory. Typically, such a memory specifies that if one port is
|
||||||
|
// writing to an address, the other must not concurrently read or
|
||||||
|
// write that same address. This means the arbiters attached to
|
||||||
|
// each memory port must cooperate to avoid simultaneously granting
|
||||||
|
// conflicting requests from their clients.
|
||||||
|
//
|
||||||
|
// Calling forbid_addr prevents the arbiter from granting a
|
||||||
|
// concurrent request to access the given address. forbidden_addr
|
||||||
|
// emits the address for which a write access is being granted.
|
||||||
|
//
|
||||||
|
// MemArbiter intances are Connectable: mkConnection(a, b) gives
|
||||||
|
// conflict priority to a. That is, b will not grant requests that
|
||||||
|
// conflict with the grant that a has emitted.
|
||||||
method Action forbid_addr(addr addr);
|
method Action forbid_addr(addr addr);
|
||||||
method addr forbidden_addr();
|
method addr forbidden_addr();
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
|
instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr));
|
||||||
|
module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc);
|
||||||
|
(* fire_when_enabled *)
|
||||||
|
rule forward_forbid;
|
||||||
|
b.forbid_addr(a.forbidden_addr);
|
||||||
|
endrule
|
||||||
|
endmodule
|
||||||
|
endinstance
|
||||||
|
|
||||||
|
// mkPriorityMemArbiter returns a MemArbiter that gives priority to
|
||||||
|
// lower numbered ports.
|
||||||
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
||||||
provisos (Bits#(addr, _),
|
provisos (Bits#(addr, _),
|
||||||
Eq#(addr),
|
Eq#(addr),
|
||||||
|
@ -98,8 +129,9 @@ typedef struct {
|
||||||
Maybe#(addr) blocked_addr;
|
Maybe#(addr) blocked_addr;
|
||||||
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
|
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
|
||||||
|
|
||||||
|
// select_grant computes which one entry of requests should be granted.
|
||||||
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
|
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
|
||||||
UInt#(TLog#(n)) lopri,
|
UInt#(TLog#(n)) hipri,
|
||||||
Maybe#(addr) block_addr)
|
Maybe#(addr) block_addr)
|
||||||
provisos (Eq#(addr));
|
provisos (Eq#(addr));
|
||||||
|
|
||||||
|
@ -130,7 +162,7 @@ function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let in = zip(map(fromInteger, genVector()), requests);
|
let in = zip(map(fromInteger, genVector()), requests);
|
||||||
let rot = reverse(rotateBy(reverse(in), lopri));
|
let rot = rotateBy(in, fromInteger(valueOf(n)-1)-hipri+1);
|
||||||
let seed = GrantResult{
|
let seed = GrantResult{
|
||||||
granted: False,
|
granted: False,
|
||||||
grant_vec: replicate(False),
|
grant_vec: replicate(False),
|
||||||
|
@ -151,10 +183,11 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
||||||
RWire#(addr) blocked_in <- mkRWire();
|
RWire#(addr) blocked_in <- mkRWire();
|
||||||
Wire#(Maybe#(addr)) blocked_out <- mkBypassWire();
|
Wire#(Maybe#(addr)) blocked_out <- mkBypassWire();
|
||||||
|
|
||||||
// low_priority is the index of the client that should be last in
|
// high_prio is the index of the client that should be first in
|
||||||
// line to receive access. Every time we grant access to a client,
|
// line to receive access. Every time we grant access to a client,
|
||||||
// that client becomes low_priority for the next round.
|
// the one after that in sequence becomes high_prio in the next
|
||||||
Reg#(UInt#(TLog#(num_clients))) low_priority <- mkReg(0);
|
// round.
|
||||||
|
Reg#(UInt#(TLog#(num_clients))) high_prio <- mkReg(0);
|
||||||
|
|
||||||
function Maybe#(_t) get_mreq(RWire#(_t) w);
|
function Maybe#(_t) get_mreq(RWire#(_t) w);
|
||||||
return w.wget();
|
return w.wget();
|
||||||
|
@ -162,11 +195,14 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
||||||
|
|
||||||
rule grant;
|
rule grant;
|
||||||
let in = map(get_mreq, reqs);
|
let in = map(get_mreq, reqs);
|
||||||
let res = select_grant(in, low_priority, blocked_in.wget());
|
let res = select_grant(in, high_prio, blocked_in.wget());
|
||||||
|
|
||||||
grants <= res.grant_vec;
|
grants <= res.grant_vec;
|
||||||
if (res.granted)
|
if (res.granted)
|
||||||
low_priority <= res.selected+1;
|
if (res.selected == fromInteger(valueOf(num_clients)-1))
|
||||||
|
high_prio <= 0;
|
||||||
|
else
|
||||||
|
high_prio <= res.selected+1;
|
||||||
blocked_out <= res.blocked_addr;
|
blocked_out <= res.blocked_addr;
|
||||||
endrule
|
endrule
|
||||||
|
|
||||||
|
|
|
@ -227,77 +227,89 @@ module mkTB(Empty);
|
||||||
let rrTests = vec(
|
let rrTests = vec(
|
||||||
// Simple grants
|
// Simple grants
|
||||||
testCase("All idle",
|
testCase("All idle",
|
||||||
vec(idle, idle, idle), noForbid,
|
vec(idle, idle, idle, idle), noForbid,
|
||||||
noGrant, noForbid),
|
noGrant, noForbid),
|
||||||
testCase("Port 0 read",
|
testCase("Port 0 read",
|
||||||
vec(read(1), idle, idle), noForbid,
|
vec(read(1), idle, idle, idle), noForbid,
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("Port 0 write",
|
testCase("Port 0 write",
|
||||||
vec(write(1), idle, idle), noForbid,
|
vec(write(1), idle, idle, idle), noForbid,
|
||||||
grant(0), forbid(1)),
|
grant(0), forbid(1)),
|
||||||
testCase("Port 1 read",
|
testCase("Port 1 read",
|
||||||
vec(idle, read(1), idle), noForbid,
|
vec(idle, read(1), idle, idle), noForbid,
|
||||||
grant(1), noForbid),
|
grant(1), noForbid),
|
||||||
testCase("Port 1 write",
|
testCase("Port 1 write",
|
||||||
vec(idle, write(1), idle), noForbid,
|
vec(idle, write(1), idle, idle), noForbid,
|
||||||
grant(1), forbid(1)),
|
grant(1), forbid(1)),
|
||||||
testCase("Port 2 read",
|
testCase("Port 2 read",
|
||||||
vec(idle, idle, read(1)), noForbid,
|
vec(idle, idle, read(1), idle), noForbid,
|
||||||
grant(2), noForbid),
|
grant(2), noForbid),
|
||||||
testCase("Port 2 write",
|
testCase("Port 2 write",
|
||||||
vec(idle, idle, write(1)), noForbid,
|
vec(idle, idle, write(1), idle), noForbid,
|
||||||
grant(2), forbid(1)),
|
grant(2), forbid(1)),
|
||||||
|
testCase("Port 3 read",
|
||||||
|
vec(idle, idle, idle, read(1)), noForbid,
|
||||||
|
grant(3), noForbid),
|
||||||
|
testCase("Port 3 write",
|
||||||
|
vec(idle, idle, idle, write(1)), noForbid,
|
||||||
|
grant(3), forbid(1)),
|
||||||
|
|
||||||
// Priorities
|
// Priorities
|
||||||
testCase("Port 2 to reset RR",
|
testCase("Port 3 to reset RR",
|
||||||
vec(idle, idle, read(1)), noForbid,
|
vec(idle, idle, idle, read(1)), noForbid,
|
||||||
grant(2), noForbid),
|
grant(3), noForbid),
|
||||||
testCase("Port 0+1 #1",
|
testCase("Port 0+1 #1",
|
||||||
vec(read(1), read(2), idle), noForbid,
|
vec(read(1), read(2), idle, idle), noForbid,
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("Port 0+1 #2",
|
testCase("Port 0+1 #2",
|
||||||
vec(read(1), read(2), idle), noForbid,
|
vec(read(1), read(2), idle, idle), noForbid,
|
||||||
grant(1), noForbid),
|
grant(1), noForbid),
|
||||||
testCase("Port 0+1 #3",
|
testCase("Port 0+1 #3",
|
||||||
vec(read(1), read(2), idle), noForbid,
|
vec(read(1), read(2), idle, idle), noForbid,
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("Port 0+2 #1",
|
testCase("Port 0+2 #1",
|
||||||
vec(read(1), idle, read(2)), noForbid,
|
vec(read(1), idle, read(2), idle), noForbid,
|
||||||
grant(2), noForbid),
|
grant(2), noForbid),
|
||||||
testCase("Port 0+2 #2",
|
testCase("Port 0+2 #2",
|
||||||
vec(read(1), idle, read(2)), noForbid,
|
vec(read(1), idle, read(2), idle), noForbid,
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("Port 0+1+2 #1",
|
testCase("Port 0+1+2+3 #1",
|
||||||
vec(read(1), read(2), read(3)), noForbid,
|
vec(read(1), read(2), read(3), read(4)), noForbid,
|
||||||
grant(1), noForbid),
|
grant(1), noForbid),
|
||||||
testCase("Port 0+1+2 #2",
|
testCase("Port 0+1+2+3 #2",
|
||||||
vec(read(1), read(2), read(3)), noForbid,
|
vec(read(1), read(2), read(3), read(4)), noForbid,
|
||||||
grant(2), noForbid),
|
grant(2), noForbid),
|
||||||
testCase("Port 0+1+2 #3",
|
testCase("Port 0+1+2+3 #3",
|
||||||
vec(read(1), read(2), read(3)), noForbid,
|
vec(read(1), read(2), read(3), read(4)), noForbid,
|
||||||
|
grant(3), noForbid),
|
||||||
|
testCase("Port 0+1+2+3 #4",
|
||||||
|
vec(read(1), read(2), read(3), read(4)), noForbid,
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("Port 0+1+2 #4",
|
testCase("Port 0+1+2+3 #5",
|
||||||
vec(read(1), read(2), read(3)), noForbid,
|
vec(read(1), read(2), read(3), read(4)), noForbid,
|
||||||
grant(1), noForbid),
|
grant(1), noForbid),
|
||||||
|
|
||||||
// Forbidden addrs
|
// Forbidden addrs
|
||||||
testCase("Port 2 to reset RR",
|
testCase("Port 3 to reset RR",
|
||||||
vec(idle, idle, read(1)), noForbid,
|
vec(idle, idle, idle, read(1)), noForbid,
|
||||||
grant(2), noForbid),
|
grant(3), noForbid),
|
||||||
testCase("RR with denied writes #1",
|
testCase("RR with denied writes #1",
|
||||||
vec(read(1), write(2), read(3)), forbid(3),
|
vec(read(1), write(2), read(3), read(4)), forbid(3),
|
||||||
grant(0), noForbid),
|
grant(0), noForbid),
|
||||||
testCase("RR with denied writes #2",
|
testCase("RR with denied writes #2",
|
||||||
vec(read(1), write(2), read(3)), forbid(3),
|
vec(read(1), write(2), read(3), read(4)), forbid(3),
|
||||||
grant(1), forbid(2)),
|
grant(1), forbid(2)),
|
||||||
testCase("RR with denied writes #3",
|
testCase("RR with denied writes #3",
|
||||||
vec(read(1), write(2), read(3)), forbid(3),
|
vec(read(1), write(2), read(3), read(4)), forbid(3),
|
||||||
grant(0), noForbid),
|
grant(3), noForbid),
|
||||||
testCase("RR with denied writes #4",
|
testCase("RR with denied writes #4",
|
||||||
vec(read(1), write(2), read(3)), forbid(3),
|
vec(read(1), write(2), read(3), read(4)), forbid(3),
|
||||||
|
grant(0), noForbid),
|
||||||
|
testCase("RR with denied writes #5",
|
||||||
|
vec(read(1), write(2), read(3), read(4)), forbid(3),
|
||||||
grant(1), forbid(2))
|
grant(1), forbid(2))
|
||||||
);
|
);
|
||||||
MemArbiter#(3, Addr) rr <- mkRoundRobinMemArbiter();
|
MemArbiter#(4, Addr) rr <- mkRoundRobinMemArbiter();
|
||||||
let rrTB <- mkArbiterTB(rr, rrTests);
|
let rrTB <- mkArbiterTB(rr, rrTests);
|
||||||
|
|
||||||
runTest(100,
|
runTest(100,
|
||||||
|
|
Loading…
Reference in New Issue