diff --git a/vram/MemArbiter.bsv b/vram/MemArbiter.bsv index 1363464..bf61806 100644 --- a/vram/MemArbiter.bsv +++ b/vram/MemArbiter.bsv @@ -14,6 +14,8 @@ typedef struct { 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)); @@ -35,34 +37,34 @@ 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 + // 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 conflict prevents the arbiter from granting a concurrent - // request that would result in a write-write, read-write or - // write-read conflict. granted_op emits the operation that the - // arbiter is granting, if any. + // 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. // - // MemArbiter intances are Connectable: mkConnection(a, b) gives - // conflict priority to a. That is, b only grants requests that - // don't conflict with a's grant. + // 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(MemArbiterOp#(addr) conflict); - method MemArbiterOp#(addr) granted_op(); + 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); - (* fire_when_enabled *) - rule forward_conflict; - b.conflict(a.granted_op); - endrule + mkConnection(a.conflict_out, b.conflict_in); endmodule endinstance @@ -71,13 +73,14 @@ endinstance module mkPriorityMemArbiter(MemArbiter#(num_clients, addr)) provisos (Bits#(addr, _), Eq#(addr), - Min#(num_clients, 1, 1)); + 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_in <- mkRWire(); - RWire#(MemArbiterOp#(addr)) granted_op_out <- mkRWire(); + RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire(); + RWire#(client_idx) granted_idx <- mkRWire(); (* no_implicit_conditions, fire_when_enabled *) rule grant_requests; @@ -86,11 +89,11 @@ module mkPriorityMemArbiter(MemArbiter#(num_clients, addr)) for (Integer i=0; i