package MemArbiter; import Connectable::*; import Vector::*; export MemArbiterOp(..); export MemArbiterServer(..); export MemArbiterClient(..); export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter; // A MemArbiterOp is an operation that a client is seeking permission // to perform. typedef struct { Bool write; addr addr; } MemArbiterOp#(type addr) deriving (Bits, Eq, FShow); // A MemArbiterServer receives requests and emits grants. interface MemArbiterServer#(type addr); method Action request(MemArbiterOp#(addr) req); method Bool grant(); endinterface // A MemArbiterClient emits requests and receives grants. interface MemArbiterClient#(type addr); method Maybe#(MemArbiterOp#(addr)) request(); method Action grant(); endinterface // Arbiter clients and servers can be connected in the obvious way. 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 // A MemArbiter manages concurrent access to a memory port. interface MemArbiter#(numeric type num_clients, type addr); // ports allow clients to request memory access. 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 addr forbidden_addr(); 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)) 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