2024-08-31 22:25:04 +02:00
|
|
|
package MemArbiter;
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 04:40:02 +02:00
|
|
|
import Connectable::*;
|
2024-08-31 22:25:04 +02:00
|
|
|
import Vector::*;
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
export MemArbiterOp(..);
|
2024-09-08 01:52:26 +02:00
|
|
|
export MemArbiterServer(..);
|
2024-09-08 20:42:35 +02:00
|
|
|
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
|
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
// A MemArbiterOp is an operation that a client is seeking permission
|
|
|
|
// to perform.
|
2024-09-08 20:42:35 +02:00
|
|
|
typedef struct {
|
|
|
|
Bool write;
|
|
|
|
addr addr;
|
|
|
|
} MemArbiterOp#(type addr) deriving (Bits, Eq, FShow);
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 18:26:59 +02:00
|
|
|
// mem_ops_conflict reports whether memory accesses a and b would
|
|
|
|
// cause undefined behavior if they proceed simultaneously.
|
2024-09-08 23:49:54 +02:00
|
|
|
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
|
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
// A MemArbiterServer receives requests and emits grants.
|
2024-09-09 00:06:27 +02:00
|
|
|
(* always_ready *)
|
2024-09-08 20:42:35 +02:00
|
|
|
interface MemArbiterServer#(type addr);
|
|
|
|
method Action request(MemArbiterOp#(addr) req);
|
2024-08-31 22:25:04 +02:00
|
|
|
method Bool grant();
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
// A MemArbiter manages concurrent access to a memory port.
|
2024-09-08 20:42:35 +02:00
|
|
|
interface MemArbiter#(numeric type num_clients, type addr);
|
2024-09-08 22:31:53 +02:00
|
|
|
// ports allow clients to request memory access.
|
2024-09-08 20:42:35 +02:00
|
|
|
interface Vector#(num_clients, MemArbiterServer#(addr)) ports;
|
2024-09-08 22:31:53 +02:00
|
|
|
|
2024-09-08 18:26:59 +02:00
|
|
|
// granted_port returns the index in ports of the client that is
|
|
|
|
// being granted its request.
|
|
|
|
method UInt#(TLog#(num_clients)) granted_port();
|
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
// The following methods are to support arbiter chaining.
|
|
|
|
//
|
2024-09-08 18:26:59 +02:00
|
|
|
// 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
|
2024-09-08 22:31:53 +02:00
|
|
|
// conflicting requests from their clients.
|
|
|
|
//
|
2024-09-08 18:26:59 +02:00
|
|
|
// 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.
|
2024-09-08 22:31:53 +02:00
|
|
|
//
|
2024-09-08 18:26:59 +02:00
|
|
|
// mkConnection(firstArbiter, secondArbiter) gives conflict
|
|
|
|
// priority to firstArbiter. That is, secondArbiter only grants
|
|
|
|
// requests that don't conflict with grants made by firstArbiter.
|
2024-09-09 00:06:27 +02:00
|
|
|
(* always_ready *)
|
2024-09-08 18:26:59 +02:00
|
|
|
method Action conflict_in(MemArbiterOp#(addr) conflict);
|
|
|
|
method MemArbiterOp#(addr) conflict_out();
|
2024-09-08 20:42:35 +02:00
|
|
|
endinterface
|
2024-09-08 01:52:26 +02:00
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr));
|
|
|
|
module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc);
|
2024-09-08 18:26:59 +02:00
|
|
|
mkConnection(a.conflict_out, b.conflict_in);
|
2024-09-08 22:31:53 +02:00
|
|
|
endmodule
|
|
|
|
endinstance
|
|
|
|
|
|
|
|
// mkPriorityMemArbiter returns a MemArbiter that gives priority to
|
|
|
|
// lower numbered ports.
|
2024-09-08 20:42:35 +02:00
|
|
|
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
|
|
|
provisos (Bits#(addr, _),
|
|
|
|
Eq#(addr),
|
2024-09-08 18:26:59 +02:00
|
|
|
Min#(num_clients, 1, 1),
|
|
|
|
Alias#(client_idx, UInt#(TLog#(num_clients))));
|
2024-09-08 01:52:26 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire());
|
|
|
|
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 18:26:59 +02:00
|
|
|
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire();
|
|
|
|
RWire#(client_idx) granted_idx <- mkRWire();
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
|
|
rule grant_requests;
|
|
|
|
Vector#(num_clients, Bool) grant = replicate(False);
|
|
|
|
Bool done = False;
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
for (Integer i=0; i<valueOf(num_clients); i=i+1) begin
|
2024-09-08 23:49:54 +02:00
|
|
|
if (reqs[i].wget() matches tagged Valid .req &&&
|
2024-09-08 18:26:59 +02:00
|
|
|
!mem_ops_conflict(conflict_op.wget(), reqs[i].wget()) &&&
|
2024-09-08 23:49:54 +02:00
|
|
|
!done) begin
|
|
|
|
done = True;
|
|
|
|
grant[i] = True;
|
2024-09-08 18:26:59 +02:00
|
|
|
granted_idx.wset(fromInteger(i));
|
2024-09-08 23:49:54 +02:00
|
|
|
end
|
2024-08-31 22:25:04 +02:00
|
|
|
end
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
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;
|
2024-09-08 18:26:59 +02:00
|
|
|
method client_idx granted_port() if (granted_idx.wget() matches tagged Valid .idx);
|
|
|
|
return idx;
|
|
|
|
endmethod
|
|
|
|
method MemArbiterOp#(addr) conflict_out() if (granted_idx.wget() matches tagged Valid .idx &&&
|
|
|
|
reqs[idx].wget() matches tagged Valid .op);
|
2024-09-08 23:49:54 +02:00
|
|
|
return op;
|
2024-09-08 20:42:35 +02:00
|
|
|
endmethod
|
2024-09-08 18:26:59 +02:00
|
|
|
method conflict_in = conflict_op.wset;
|
2024-09-08 20:42:35 +02:00
|
|
|
endmodule
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
Vector#(n, Bool) grant_vec;
|
2024-09-08 18:26:59 +02:00
|
|
|
Maybe#(UInt#(TLog#(n))) granted_idx;
|
2024-09-08 20:42:35 +02:00
|
|
|
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
|
|
|
|
|
2024-09-09 00:06:27 +02:00
|
|
|
// select_grant computes which one entry of requests should be
|
|
|
|
// granted. Priority order is descending starting from
|
|
|
|
// requests[hipri].
|
2024-09-08 20:42:35 +02:00
|
|
|
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
|
2024-09-08 18:26:59 +02:00
|
|
|
client_idx hipri,
|
2024-09-08 23:49:54 +02:00
|
|
|
Maybe#(MemArbiterOp#(addr)) conflict)
|
2024-09-08 18:26:59 +02:00
|
|
|
provisos (Eq#(addr),
|
|
|
|
Alias#(client_idx, UInt#(TLog#(n))));
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
function onehot(idx);
|
|
|
|
let ret = replicate(False);
|
|
|
|
ret[idx] = True;
|
|
|
|
return ret;
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function GrantResult#(n, addr) do_fold(GrantResult#(n, addr) acc,
|
2024-09-08 18:26:59 +02:00
|
|
|
Tuple2#(client_idx,
|
2024-09-08 20:42:35 +02:00
|
|
|
Maybe#(MemArbiterOp#(addr))) next);
|
|
|
|
match {.idx, .mreq} = next;
|
2024-09-08 18:26:59 +02:00
|
|
|
if (mreq matches tagged Valid .req &&&
|
|
|
|
acc.granted_idx matches tagged Invalid &&&
|
|
|
|
!mem_ops_conflict(conflict, mreq))
|
2024-09-08 20:42:35 +02:00
|
|
|
return GrantResult{
|
|
|
|
grant_vec: onehot(idx),
|
2024-09-08 18:26:59 +02:00
|
|
|
granted_idx: tagged Valid idx
|
2024-09-08 20:42:35 +02:00
|
|
|
};
|
|
|
|
else
|
|
|
|
// Previous grant won, not requesting, or request not satisfiable.
|
|
|
|
return acc;
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
let in = zip(map(fromInteger, genVector()), requests);
|
2024-09-08 22:31:53 +02:00
|
|
|
let rot = rotateBy(in, fromInteger(valueOf(n)-1)-hipri+1);
|
2024-09-08 20:42:35 +02:00
|
|
|
let seed = GrantResult{
|
|
|
|
grant_vec: replicate(False),
|
2024-09-08 18:26:59 +02:00
|
|
|
granted_idx: tagged Invalid
|
2024-09-08 20:42:35 +02:00
|
|
|
};
|
|
|
|
return foldl(do_fold, seed, rot);
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
|
|
|
provisos (Bits#(addr, _),
|
|
|
|
Eq#(addr),
|
2024-09-08 18:26:59 +02:00
|
|
|
Min#(num_clients, 1, 1),
|
|
|
|
Alias#(client_idx, UInt#(TLog#(num_clients))));
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire);
|
|
|
|
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
|
|
|
|
2024-09-08 18:26:59 +02:00
|
|
|
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire();
|
|
|
|
RWire#(client_idx) granted_idx_out <- mkRWire();
|
2024-09-08 20:42:35 +02:00
|
|
|
|
2024-09-08 22:31:53 +02:00
|
|
|
// high_prio is the index of the client that should be first in
|
2024-09-08 20:42:35 +02:00
|
|
|
// line to receive access. Every time we grant access to a client,
|
2024-09-08 22:31:53 +02:00
|
|
|
// the one after that in sequence becomes high_prio in the next
|
|
|
|
// round.
|
2024-09-08 18:26:59 +02:00
|
|
|
Reg#(client_idx) high_prio <- mkReg(0);
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
function Maybe#(_t) get_mreq(RWire#(_t) w);
|
|
|
|
return w.wget();
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
rule grant;
|
|
|
|
let in = map(get_mreq, reqs);
|
2024-09-08 18:26:59 +02:00
|
|
|
let res = select_grant(in, high_prio, conflict_op.wget());
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
grants <= res.grant_vec;
|
2024-09-08 18:26:59 +02:00
|
|
|
if (res.granted_idx matches tagged Valid .idx) begin
|
|
|
|
granted_idx_out.wset(idx);
|
2024-09-09 00:06:27 +02:00
|
|
|
high_prio <= validValue(findElem(True, rotateR(res.grant_vec)));
|
|
|
|
end
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
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;
|
2024-09-08 18:26:59 +02:00
|
|
|
method client_idx granted_port() if (granted_idx_out.wget() matches tagged Valid .idx);
|
|
|
|
return idx;
|
|
|
|
endmethod
|
|
|
|
method MemArbiterOp#(addr) conflict_out() if (granted_idx_out.wget() matches tagged Valid .idx &&&
|
|
|
|
reqs[idx].wget() matches tagged Valid .op);
|
2024-09-08 23:49:54 +02:00
|
|
|
return op;
|
2024-09-08 20:42:35 +02:00
|
|
|
endmethod
|
2024-09-08 18:26:59 +02:00
|
|
|
method conflict_in = conflict_op.wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
endmodule
|
|
|
|
|
|
|
|
endpackage
|