vram/MemArbiter: rewrite to use client/server idioms

In preparation for making the two Connectable and defining an
arbitrated memory client/server for VRAM access.
This commit is contained in:
David Anderson 2024-09-07 16:52:26 -07:00
parent b2b2c14009
commit aa048537ef
3 changed files with 66 additions and 87 deletions

View File

@ -20,26 +20,21 @@ interface Top;
method Bit#(6) grants(); method Bit#(6) grants();
endinterface endinterface
typedef struct {
Bool write;
Addr address;
} WriteReq deriving (Bits, Eq);
(* synthesize, clock_prefix="clk_25mhz", reset_prefix="rst_btn" *) (* synthesize, clock_prefix="clk_25mhz", reset_prefix="rst_btn" *)
module mkTop(Top); module mkTop(Top);
Vector#(2, Reg#(Maybe#(WriteReq))) wrin <- replicateM(mkDReg(tagged Invalid)); Vector#(2, Reg#(Maybe#(MemArbiterWrite#(Addr)))) wrin <- replicateM(mkDReg(tagged Invalid));
Vector#(4, Reg#(Maybe#(Addr))) rdin <- replicateM(mkDReg(tagged Invalid)); Vector#(4, Reg#(Maybe#(Addr))) rdin <- replicateM(mkDReg(tagged Invalid));
MemArbiter#(Addr) ret <- mkMemArbiter(); MemArbiter#(Addr) ret <- mkMemArbiter();
Reg#(Vector#(6, Bool)) ok <- mkReg(replicate(False)); Reg#(Vector#(6, Bool)) ok <- mkReg(replicate(False));
rule req_cpu (wrin[0] matches tagged Valid .req &&& req matches WriteReq{write: .write, address: .addr}); rule req_cpu (wrin[0] matches tagged Valid .req);
ret.cpu.request(write, addr); ret.cpu.request(req);
endrule endrule
rule req_debugger (wrin[1] matches tagged Valid .req &&& req matches WriteReq{write: .write, address: .addr}); rule req_debugger (wrin[1] matches tagged Valid .req);
ret.debugger.request(write, addr); ret.debugger.request(req);
endrule endrule
rule req_palette (rdin[0] matches tagged Valid .addr); rule req_palette (rdin[0] matches tagged Valid .addr);
@ -70,11 +65,11 @@ module mkTop(Top);
endrule endrule
method Action cpu(Bool write, Addr addr); method Action cpu(Bool write, Addr addr);
wrin[0] <= tagged Valid WriteReq{write: write, address: addr}; wrin[0] <= tagged Valid MemArbiterWrite{write: write, addr: addr};
endmethod endmethod
method Action debugger(Bool write, Addr addr); method Action debugger(Bool write, Addr addr);
wrin[1] <= tagged Valid WriteReq{write: write, address: addr}; wrin[1] <= tagged Valid MemArbiterWrite{write: write, addr: addr};
endmethod endmethod
method Action palette(Addr addr); method Action palette(Addr addr);

View File

@ -2,54 +2,53 @@ package MemArbiter;
import Vector::*; import Vector::*;
export MemArbiterWriter(..); export MemArbiterWrite(..);
export MemArbiterReader(..); export MemArbiterServer(..);
export MemArbiterClient(..);
export MemArbiter(..); export MemArbiter(..);
export mkMemArbiter; export mkMemArbiter;
// A MemArbiterWriter can request use of a memory port to read or // A MemArbiterServer receives requests for memory access and emits
// write to an address. When a request is feasible, grant() returns // grants.
// True in the same cycle. interface MemArbiterServer#(type request);
(* always_ready *) method Action request(request req);
interface MemArbiterWriter#(type addr);
method Action request(Bool write, addr address);
method Bool grant(); method Bool grant();
endinterface endinterface
// MemArbiterReader can request use of a memory port to read from // A MemArbiterClient emits requests for memory access and emits
// an address. When a request is feasible, grant() returns True in the // grants.
// same cycle. interface MemArbiterClient#(type request);
(* always_ready *) method request request();
interface MemArbiterReader#(type addr); method Action grant();
method Action request(addr address);
method Bool grant();
endinterface endinterface
// A MemArbiter manages concurrent access to memory ports. It typedef struct {
// mediates access between 2 writers and 4 readers. Bool write;
addr addr;
} MemArbiterWrite#(type addr) deriving (Bits, Eq);
// A MemArbiter manages concurrent access to memory ports.
interface MemArbiter#(type addr); interface MemArbiter#(type addr);
// Assigned to port A. interface MemArbiterServer#(MemArbiterWrite#(addr)) cpu;
interface MemArbiterWriter#(addr) cpu; interface MemArbiterServer#(MemArbiterWrite#(addr)) debugger;
interface MemArbiterWriter#(addr) debugger; interface MemArbiterServer#(addr) palette;
interface MemArbiterReader#(addr) palette;
// Assigned to port B. interface MemArbiterServer#(addr) tile1;
interface MemArbiterReader#(addr) tile1; interface MemArbiterServer#(addr) tile2;
interface MemArbiterReader#(addr) tile2; interface MemArbiterServer#(addr) sprite;
interface MemArbiterReader#(addr) sprite;
endinterface endinterface
// mkMemArbiter builds a GARY memory arbiter. // mkMemArbiter builds a GARY memory arbiter.
// //
// Port A arbitrates with strict priority: CPU requests always proceed // Port A arbitrates with strict priority: CPU requests go first, then
// first, followed by debugger requests, then the palette DAC. // the debugger, then the palette DAC.
// //
// Port B does round-robin arbitration, giving each client a fair // Port B does round-robin arbitration, giving each client a fair
// chance of having its requests processed. // share of memory access.
module mkMemArbiter(MemArbiter#(addr) ifc) module mkMemArbiter(MemArbiter#(addr))
provisos(Bits#(addr, _), provisos(Bits#(addr, _),
Eq#(addr), Eq#(addr),
Alias#(write_req, Tuple2#(Bool, addr))); Alias#(write_req, MemArbiterWrite#(addr)));
////// //////
// Port A users // Port A users
@ -73,16 +72,16 @@ module mkMemArbiter(MemArbiter#(addr) ifc)
(* preempts = "grant_debugger, grant_palette" *) (* preempts = "grant_debugger, grant_palette" *)
(* fire_when_enabled *) (* fire_when_enabled *)
rule grant_cpu (cpu_req.wget matches tagged Valid {.write, .addr}); rule grant_cpu (cpu_req.wget matches tagged Valid .req);
cpu_ok.send(); cpu_ok.send();
if (write) if (req.write)
written_addr.wset(addr); written_addr.wset(req.addr);
endrule endrule
rule grant_debugger (debugger_req.wget matches tagged Valid {.write, .addr}); rule grant_debugger (debugger_req.wget matches tagged Valid .req);
debugger_ok.send(); debugger_ok.send();
if (write) if (req.write)
written_addr.wset(addr); written_addr.wset(req.addr);
endrule endrule
rule grant_palette (palette_req); rule grant_palette (palette_req);
@ -143,45 +142,35 @@ module mkMemArbiter(MemArbiter#(addr) ifc)
////// //////
// External interface // External interface
interface MemArbiterWriter cpu; interface MemArbiterServer cpu;
method Action request(write, addr); method request = cpu_req.wset;
cpu_req.wset(tuple2(write, addr));
endmethod
method grant = cpu_ok; method grant = cpu_ok;
endinterface endinterface
interface MemArbiterWriter debugger; interface MemArbiterServer debugger;
method Action request(write, addr); method request = debugger_req.wset;
debugger_req.wset(tuple2(write, addr));
endmethod
method grant = debugger_ok; method grant = debugger_ok;
endinterface endinterface
interface MemArbiterReader palette; interface MemArbiterServer palette;
method Action request(addr); method Action request(addr);
palette_req.send(); palette_req.send();
endmethod endmethod
method grant = palette_ok; method grant = palette_ok;
endinterface endinterface
interface MemArbiterReader tile1; interface MemArbiterServer tile1;
method Action request(addr); method request = portB_req[0].wset;
portB_req[0].wset(addr);
endmethod
method grant = portB_grant[0]; method grant = portB_grant[0];
endinterface endinterface
interface MemArbiterReader tile2; interface MemArbiterServer tile2;
method Action request(addr); method request = portB_req[1].wset;
portB_req[1].wset(addr);
endmethod
method grant = portB_grant[1]; method grant = portB_grant[1];
endinterface endinterface
interface MemArbiterReader sprite; interface MemArbiterServer sprite;
method Action request(addr); method request = portB_req[2].wset;
portB_req[2].wset(addr);
endmethod
method grant = portB_grant[2]; method grant = portB_grant[2];
endinterface endinterface
endmodule endmodule

View File

@ -12,15 +12,10 @@ import MemArbiter::*;
typedef UInt#(4) Addr; typedef UInt#(4) Addr;
typedef struct {
Bool write;
Addr addr;
} WriteReq deriving (Bits, Eq);
typedef struct { typedef struct {
String name; String name;
Maybe#(WriteReq) cpu; Maybe#(MemArbiterWrite#(Addr)) cpu;
Maybe#(WriteReq) debugger; Maybe#(MemArbiterWrite#(Addr)) debugger;
Maybe#(Addr) palette; Maybe#(Addr) palette;
Maybe#(Addr) tile1; Maybe#(Addr) tile1;
Maybe#(Addr) tile2; Maybe#(Addr) tile2;
@ -29,12 +24,12 @@ typedef struct {
Vector#(6, Bool) want; Vector#(6, Bool) want;
} TestCase deriving (Bits, Eq); } TestCase deriving (Bits, Eq);
function Maybe#(WriteReq) rwRead(Addr addr); function Maybe#(MemArbiterWrite#(Addr)) rwRead(Addr addr);
return tagged Valid WriteReq{write: False, addr: addr}; return tagged Valid MemArbiterWrite{write: False, addr: addr};
endfunction endfunction
function Maybe#(WriteReq) rwWrite(Addr addr); function Maybe#(MemArbiterWrite#(Addr)) rwWrite(Addr addr);
return tagged Valid WriteReq{write: True, addr: addr}; return tagged Valid MemArbiterWrite{write: True, addr: addr};
endfunction endfunction
function Maybe#(Addr) read(Addr addr); function Maybe#(Addr) read(Addr addr);
@ -55,8 +50,8 @@ function Vector#(6, Bool) grant(Integer granted_a, Integer granted_b);
endfunction endfunction
function TestCase testCase(String name, function TestCase testCase(String name,
Maybe#(WriteReq) cpu, Maybe#(MemArbiterWrite#(Addr)) cpu,
Maybe#(WriteReq) debugger, Maybe#(MemArbiterWrite#(Addr)) debugger,
Maybe#(Addr) palette, Maybe#(Addr) palette,
Maybe#(Addr) tile1, Maybe#(Addr) tile1,
Maybe#(Addr) tile2, Maybe#(Addr) tile2,
@ -209,13 +204,13 @@ module mkTB();
endrule endrule
(* no_implicit_conditions, fire_when_enabled *) (* no_implicit_conditions, fire_when_enabled *)
rule input_cpu (tests[idx].cpu matches tagged Valid .req &&& req matches WriteReq {write: .write, addr: .addr}); rule input_cpu (tests[idx].cpu matches tagged Valid .req);
dut.cpu.request(write, addr); dut.cpu.request(req);
endrule endrule
(* no_implicit_conditions, fire_when_enabled *) (* no_implicit_conditions, fire_when_enabled *)
rule input_debugger (tests[idx].debugger matches tagged Valid .req &&& req matches WriteReq {write: .write, addr: .addr}); rule input_debugger (tests[idx].debugger matches tagged Valid .req);
dut.debugger.request(write, addr); dut.debugger.request(req);
endrule endrule
(* no_implicit_conditions, fire_when_enabled *) (* no_implicit_conditions, fire_when_enabled *)
@ -238,7 +233,7 @@ module mkTB();
dut.sprite.request(addr); dut.sprite.request(addr);
endrule endrule
function Fmt rw_str(Maybe#(WriteReq) v); function Fmt rw_str(Maybe#(MemArbiterWrite#(Addr)) v);
case (v) matches case (v) matches
tagged Valid .req: begin tagged Valid .req: begin
if (req.write) if (req.write)