package MemArbiter; import Connectable::*; import Vector::*; export MemArbiterOp(..); export MemArbiterServer(..); 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); // mem_ops_conflict reports whether memory accesses a and b would // cause undefined behavior if they proceed simultaneously. function Bool mem_ops_conflict(Maybe#(MemArbiterOp#(addr)) a, Maybe#(MemArbiterOp#(addr)) b) provisos(Eq#(addr)); if (a matches tagged Valid .ar &&& b matches tagged Valid .br &&& ar.addr == br.addr) return ar.write || br.write; else return False; endfunction // A MemArbiterServer receives requests and emits grants. (* always_ready *) interface MemArbiterServer#(type addr); method Action request(MemArbiterOp#(addr) req); method Bool grant(); endinterface // 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; // granted_port returns the index in ports of the client that is // being granted its request. method UInt#(TLog#(num_clients)) granted_port(); // 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. // // conflict_in supplies an already granted operation that this // arbiter must avoid conflicting with. conflict_out emits the // operation that the arbiter is granting, if any. // // mkConnection(firstArbiter, secondArbiter) gives conflict // priority to firstArbiter. That is, secondArbiter only grants // requests that don't conflict with grants made by firstArbiter. (* always_ready *) method Action conflict_in(MemArbiterOp#(addr) conflict); method MemArbiterOp#(addr) conflict_out(); endinterface instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr)); module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc); mkConnection(a.conflict_out, b.conflict_in); 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), Alias#(client_idx, UInt#(TLog#(num_clients)))); Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire()); Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire(); RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire(); RWire#(client_idx) granted_idx <- mkRWire(); (* no_implicit_conditions, fire_when_enabled *) rule grant_requests; Vector#(num_clients, Bool) grant = replicate(False); Bool done = False; for (Integer i=0; i