188 lines
5.8 KiB
Plaintext
188 lines
5.8 KiB
Plaintext
package MemArbiter;
|
|
|
|
import Connectable::*;
|
|
import Vector::*;
|
|
|
|
export MemArbiterOp(..);
|
|
export MemArbiterServer(..);
|
|
export MemArbiterClient(..);
|
|
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
|
|
|
|
typedef struct {
|
|
Bool write;
|
|
addr addr;
|
|
} MemArbiterOp#(type addr) deriving (Bits, Eq, FShow);
|
|
|
|
// A MemArbiterServer receives requests for memory access and emits
|
|
// grants.
|
|
interface MemArbiterServer#(type addr);
|
|
method Action request(MemArbiterOp#(addr) req);
|
|
method Bool grant();
|
|
endinterface
|
|
|
|
// A MemArbiterClient emits requests for memory access and emits
|
|
// grants.
|
|
interface MemArbiterClient#(type addr);
|
|
method Maybe#(MemArbiterOp#(addr)) request();
|
|
method Action grant();
|
|
endinterface
|
|
|
|
instance Connectable#(MemArbiterClient#(addr), MemArbiterServer#(addr));
|
|
module mkConnection(MemArbiterClient#(addr) client, MemArbiterServer#(addr) server, Empty ifc);
|
|
rule send_request (client.request matches tagged Valid .req);
|
|
server.request(req);
|
|
endrule
|
|
|
|
rule send_grant (server.grant());
|
|
client.grant();
|
|
endrule
|
|
endmodule
|
|
endinstance
|
|
|
|
interface MemArbiter#(numeric type num_clients, type addr);
|
|
interface Vector#(num_clients, MemArbiterServer#(addr)) ports;
|
|
method Action forbid_addr(addr addr);
|
|
method addr forbidden_addr();
|
|
endinterface
|
|
|
|
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
|
provisos (Bits#(addr, _),
|
|
Eq#(addr),
|
|
Min#(num_clients, 1, 1));
|
|
|
|
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire());
|
|
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
|
|
|
RWire#(addr) blocked_in <- mkRWire();
|
|
RWire#(addr) blocked_out <- mkRWire();
|
|
|
|
function Bool is_blocked(addr addr);
|
|
return blocked_in.wget() == tagged Valid addr;
|
|
endfunction
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule grant_requests;
|
|
Vector#(num_clients, Bool) grant = replicate(False);
|
|
Bool done = False;
|
|
|
|
for (Integer i=0; i<valueOf(num_clients); i=i+1) begin
|
|
if (reqs[i].wget() matches tagged Valid .req &&& !is_blocked(req.addr) &&& !done) begin
|
|
done = True;
|
|
grant[i] = True;
|
|
if (req.write)
|
|
blocked_out.wset(req.addr);
|
|
end
|
|
end
|
|
|
|
grants <= grant;
|
|
endrule
|
|
|
|
Vector#(num_clients, MemArbiterServer#(addr)) _ifcs = newVector();
|
|
for (Integer i=0; i<valueOf(num_clients); i=i+1)
|
|
_ifcs[i] = (interface MemArbiterServer#(addr);
|
|
method request = reqs[i].wset;
|
|
method grant = grants[i];
|
|
endinterface);
|
|
|
|
interface ports = _ifcs;
|
|
method forbid_addr = blocked_in.wset;
|
|
method addr forbidden_addr() if (blocked_out.wget() matches tagged Valid .addr);
|
|
return addr;
|
|
endmethod
|
|
endmodule
|
|
|
|
typedef struct {
|
|
Bool granted;
|
|
Vector#(n, Bool) grant_vec;
|
|
UInt#(TLog#(n)) selected;
|
|
Maybe#(addr) blocked_addr;
|
|
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
|
|
|
|
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
|
|
UInt#(TLog#(n)) lopri,
|
|
Maybe#(addr) block_addr)
|
|
provisos (Eq#(addr));
|
|
|
|
function is_blocked(addr);
|
|
return tagged Valid addr == block_addr;
|
|
endfunction
|
|
|
|
function onehot(idx);
|
|
let ret = replicate(False);
|
|
ret[idx] = True;
|
|
return ret;
|
|
endfunction
|
|
|
|
function GrantResult#(n, addr) do_fold(GrantResult#(n, addr) acc,
|
|
Tuple2#(UInt#(TLog#(n)),
|
|
Maybe#(MemArbiterOp#(addr))) next);
|
|
match {.idx, .mreq} = next;
|
|
if (mreq matches tagged Valid .req &&& !acc.granted &&& !is_blocked(req.addr))
|
|
return GrantResult{
|
|
granted: True,
|
|
grant_vec: onehot(idx),
|
|
selected: idx,
|
|
blocked_addr: req.write ? tagged Valid req.addr : tagged Invalid
|
|
};
|
|
else
|
|
// Previous grant won, not requesting, or request not satisfiable.
|
|
return acc;
|
|
endfunction
|
|
|
|
let in = zip(map(fromInteger, genVector()), requests);
|
|
let rot = reverse(rotateBy(reverse(in), lopri));
|
|
let seed = GrantResult{
|
|
granted: False,
|
|
grant_vec: replicate(False),
|
|
selected: 0,
|
|
blocked_addr: tagged Invalid
|
|
};
|
|
return foldl(do_fold, seed, rot);
|
|
endfunction
|
|
|
|
module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
|
provisos (Bits#(addr, _),
|
|
Eq#(addr),
|
|
Min#(num_clients, 1, 1));
|
|
|
|
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire);
|
|
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
|
|
|
RWire#(addr) blocked_in <- mkRWire();
|
|
Wire#(Maybe#(addr)) blocked_out <- mkBypassWire();
|
|
|
|
// low_priority is the index of the client that should be last in
|
|
// line to receive access. Every time we grant access to a client,
|
|
// that client becomes low_priority for the next round.
|
|
Reg#(UInt#(TLog#(num_clients))) low_priority <- mkReg(0);
|
|
|
|
function Maybe#(_t) get_mreq(RWire#(_t) w);
|
|
return w.wget();
|
|
endfunction
|
|
|
|
rule grant;
|
|
let in = map(get_mreq, reqs);
|
|
let res = select_grant(in, low_priority, blocked_in.wget());
|
|
|
|
grants <= res.grant_vec;
|
|
if (res.granted)
|
|
low_priority <= res.selected+1;
|
|
blocked_out <= res.blocked_addr;
|
|
endrule
|
|
|
|
Vector#(num_clients, MemArbiterServer#(addr)) _ifcs = newVector();
|
|
for (Integer i=0; i<valueOf(num_clients); i=i+1)
|
|
_ifcs[i] = (interface MemArbiterServer#(addr);
|
|
method request = reqs[i].wset;
|
|
method grant = grants[i];
|
|
endinterface);
|
|
|
|
interface ports = _ifcs;
|
|
method forbid_addr = blocked_in.wset;
|
|
method addr forbidden_addr() if (blocked_out matches tagged Valid .addr);
|
|
return addr;
|
|
endmethod
|
|
endmodule
|
|
|
|
endpackage
|