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 01:52:26 +02:00
|
|
|
export MemArbiterWrite(..);
|
|
|
|
export MemArbiterServer(..);
|
|
|
|
export MemArbiterClient(..);
|
2024-08-31 22:25:04 +02:00
|
|
|
export MemArbiter(..);
|
|
|
|
export mkMemArbiter;
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
// A MemArbiterServer receives requests for memory access and emits
|
|
|
|
// grants.
|
|
|
|
interface MemArbiterServer#(type request);
|
|
|
|
method Action request(request req);
|
2024-08-31 22:25:04 +02:00
|
|
|
method Bool grant();
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
// A MemArbiterClient emits requests for memory access and emits
|
|
|
|
// grants.
|
|
|
|
interface MemArbiterClient#(type request);
|
|
|
|
method request request();
|
|
|
|
method Action grant();
|
2024-08-31 22:25:04 +02:00
|
|
|
endinterface
|
|
|
|
|
2024-09-08 04:40:02 +02:00
|
|
|
instance Connectable#(MemArbiterClient#(req), MemArbiterServer#(req));
|
|
|
|
module mkConnection(MemArbiterClient#(req) client, MemArbiterServer#(req) server, Empty ifc);
|
|
|
|
rule send_request;
|
|
|
|
server.request(client.request());
|
|
|
|
endrule
|
|
|
|
|
|
|
|
rule send_grant (server.grant());
|
|
|
|
client.grant();
|
|
|
|
endrule
|
|
|
|
endmodule
|
|
|
|
endinstance
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
typedef struct {
|
|
|
|
Bool write;
|
|
|
|
addr addr;
|
|
|
|
} MemArbiterWrite#(type addr) deriving (Bits, Eq);
|
|
|
|
|
|
|
|
// A MemArbiter manages concurrent access to memory ports.
|
2024-08-31 22:25:04 +02:00
|
|
|
interface MemArbiter#(type addr);
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer#(MemArbiterWrite#(addr)) cpu;
|
|
|
|
interface MemArbiterServer#(MemArbiterWrite#(addr)) debugger;
|
|
|
|
interface MemArbiterServer#(addr) palette;
|
|
|
|
|
|
|
|
interface MemArbiterServer#(addr) tile1;
|
|
|
|
interface MemArbiterServer#(addr) tile2;
|
|
|
|
interface MemArbiterServer#(addr) sprite;
|
2024-08-31 22:25:04 +02:00
|
|
|
endinterface
|
|
|
|
|
2024-08-31 22:25:04 +02:00
|
|
|
// mkMemArbiter builds a GARY memory arbiter.
|
2024-08-31 22:25:04 +02:00
|
|
|
//
|
2024-09-08 01:52:26 +02:00
|
|
|
// Port A arbitrates with strict priority: CPU requests go first, then
|
|
|
|
// the debugger, then the palette DAC.
|
2024-08-31 22:25:04 +02:00
|
|
|
//
|
|
|
|
// Port B does round-robin arbitration, giving each client a fair
|
2024-09-08 01:52:26 +02:00
|
|
|
// share of memory access.
|
|
|
|
module mkMemArbiter(MemArbiter#(addr))
|
2024-08-31 22:25:04 +02:00
|
|
|
provisos(Bits#(addr, _),
|
|
|
|
Eq#(addr),
|
2024-09-08 01:52:26 +02:00
|
|
|
Alias#(write_req, MemArbiterWrite#(addr)));
|
2024-08-31 22:25:04 +02:00
|
|
|
|
|
|
|
//////
|
|
|
|
// Port A users
|
|
|
|
|
|
|
|
RWire#(write_req) cpu_req <- mkRWire();
|
|
|
|
RWire#(write_req) debugger_req <- mkRWire();
|
|
|
|
PulseWire palette_req <- mkPulseWire();
|
|
|
|
|
|
|
|
PulseWire cpu_ok <- mkPulseWire();
|
|
|
|
PulseWire debugger_ok <- mkPulseWire();
|
|
|
|
PulseWire palette_ok <- mkPulseWire();
|
|
|
|
|
|
|
|
// Address written to by port A, if any. Used to block port B
|
|
|
|
// clients that are trying to read the same address.
|
|
|
|
RWire#(addr) written_addr <- mkRWire();
|
|
|
|
|
|
|
|
// We could be fancy with rule conditions to express the priorities
|
|
|
|
// between clients, but Bluespec has the preempts annotation to
|
|
|
|
// express the ranking directly.
|
|
|
|
(* preempts = "grant_cpu, (grant_debugger, grant_palette)" *)
|
|
|
|
(* preempts = "grant_debugger, grant_palette" *)
|
|
|
|
|
|
|
|
(* fire_when_enabled *)
|
2024-09-08 01:52:26 +02:00
|
|
|
rule grant_cpu (cpu_req.wget matches tagged Valid .req);
|
2024-08-31 22:25:04 +02:00
|
|
|
cpu_ok.send();
|
2024-09-08 01:52:26 +02:00
|
|
|
if (req.write)
|
|
|
|
written_addr.wset(req.addr);
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
rule grant_debugger (debugger_req.wget matches tagged Valid .req);
|
2024-08-31 22:25:04 +02:00
|
|
|
debugger_ok.send();
|
2024-09-08 01:52:26 +02:00
|
|
|
if (req.write)
|
|
|
|
written_addr.wset(req.addr);
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
|
|
|
|
|
|
|
rule grant_palette (palette_req);
|
|
|
|
palette_ok.send();
|
|
|
|
endrule
|
|
|
|
|
|
|
|
//////
|
|
|
|
// Port B users
|
|
|
|
|
|
|
|
Vector#(3, RWire#(addr)) portB_req <- replicateM(mkRWire);
|
|
|
|
Wire#(Vector#(3, Bool)) portB_grant <- mkBypassWire();
|
|
|
|
|
|
|
|
Vector#(3, Bool) init = replicate(False); init[0] = True;
|
|
|
|
Reg#(Vector#(3, Bool)) priority_vec <- mkReg(init);
|
|
|
|
|
|
|
|
rule grant_portB;
|
|
|
|
Vector#(3, Bool) grants = replicate(False);
|
|
|
|
Bool port_available = False;
|
|
|
|
|
|
|
|
// This algorithm is a little mystifying at first glance, but it
|
|
|
|
// works. priority_vec has one bool per client, only one of
|
|
|
|
// which is True. That True bit identifies the client with the
|
|
|
|
// highest priority on the next request.
|
|
|
|
//
|
|
|
|
// This loop goes through each client twice, using
|
|
|
|
// port_available to track whether a client can grab the port or
|
|
|
|
// not. When we start iterating, the port is marked unavailable
|
|
|
|
// until we reach the top priority client, at which point we
|
|
|
|
// mark the port available and keep scanning. That effectively
|
|
|
|
// makes the search for a requesting client start at the top
|
|
|
|
// priority one.
|
|
|
|
//
|
|
|
|
// As we loop back around a second time, the availability bool
|
|
|
|
// gets reset again, but if you take the example of the True bit
|
|
|
|
// being in the middle of the vector, and consider cases where
|
|
|
|
// the first requestor is before/after that starting point,
|
|
|
|
// you'll see that it all works out, and at the end of the loop
|
|
|
|
// we have a new bit vector where only one client is True - the
|
|
|
|
// one whose request is granted.
|
|
|
|
for (Integer i = 0; i < 6; i=i+1) begin
|
|
|
|
Integer idx = i % 3;
|
|
|
|
if (priority_vec[idx])
|
|
|
|
port_available = True;
|
|
|
|
let req = portB_req[idx].wget();
|
|
|
|
if (port_available && isValid(req) && req != written_addr.wget()) begin
|
|
|
|
port_available = False;
|
|
|
|
grants[idx] = True;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
portB_grant <= grants;
|
|
|
|
// If we granted a request, the grantee becomes the lowest
|
|
|
|
// priority client for the next round of requests. If nobody
|
|
|
|
// requested anything, keep the same priority as before.
|
|
|
|
if (any(id, grants))
|
|
|
|
priority_vec <= rotateR(grants);
|
|
|
|
endrule
|
|
|
|
|
|
|
|
//////
|
|
|
|
// External interface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer cpu;
|
|
|
|
method request = cpu_req.wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
method grant = cpu_ok;
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer debugger;
|
|
|
|
method request = debugger_req.wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
method grant = debugger_ok;
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer palette;
|
2024-08-31 22:25:04 +02:00
|
|
|
method Action request(addr);
|
|
|
|
palette_req.send();
|
|
|
|
endmethod
|
|
|
|
method grant = palette_ok;
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer tile1;
|
|
|
|
method request = portB_req[0].wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
method grant = portB_grant[0];
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer tile2;
|
|
|
|
method request = portB_req[1].wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
method grant = portB_grant[1];
|
|
|
|
endinterface
|
|
|
|
|
2024-09-08 01:52:26 +02:00
|
|
|
interface MemArbiterServer sprite;
|
|
|
|
method request = portB_req[2].wset;
|
2024-08-31 22:25:04 +02:00
|
|
|
method grant = portB_grant[2];
|
|
|
|
endinterface
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
endpackage
|