From aa048537ef2d15529dfb3bd61211aac98fbf5725 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 7 Sep 2024 16:52:26 -0700 Subject: [PATCH] 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. --- experiments/arbiter/Top.bsv | 19 +++---- vram/MemArbiter.bsv | 103 ++++++++++++++++-------------------- vram/MemArbiter_Test.bsv | 31 +++++------ 3 files changed, 66 insertions(+), 87 deletions(-) diff --git a/experiments/arbiter/Top.bsv b/experiments/arbiter/Top.bsv index c9aba69..de48446 100644 --- a/experiments/arbiter/Top.bsv +++ b/experiments/arbiter/Top.bsv @@ -20,26 +20,21 @@ interface Top; method Bit#(6) grants(); endinterface -typedef struct { - Bool write; - Addr address; -} WriteReq deriving (Bits, Eq); - (* synthesize, clock_prefix="clk_25mhz", reset_prefix="rst_btn" *) 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)); MemArbiter#(Addr) ret <- mkMemArbiter(); Reg#(Vector#(6, Bool)) ok <- mkReg(replicate(False)); - rule req_cpu (wrin[0] matches tagged Valid .req &&& req matches WriteReq{write: .write, address: .addr}); - ret.cpu.request(write, addr); + rule req_cpu (wrin[0] matches tagged Valid .req); + ret.cpu.request(req); endrule - rule req_debugger (wrin[1] matches tagged Valid .req &&& req matches WriteReq{write: .write, address: .addr}); - ret.debugger.request(write, addr); + rule req_debugger (wrin[1] matches tagged Valid .req); + ret.debugger.request(req); endrule rule req_palette (rdin[0] matches tagged Valid .addr); @@ -70,11 +65,11 @@ module mkTop(Top); endrule 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 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 method Action palette(Addr addr); diff --git a/vram/MemArbiter.bsv b/vram/MemArbiter.bsv index 8fcc960..356d7b4 100644 --- a/vram/MemArbiter.bsv +++ b/vram/MemArbiter.bsv @@ -2,54 +2,53 @@ package MemArbiter; import Vector::*; -export MemArbiterWriter(..); -export MemArbiterReader(..); +export MemArbiterWrite(..); +export MemArbiterServer(..); +export MemArbiterClient(..); export MemArbiter(..); export mkMemArbiter; -// A MemArbiterWriter can request use of a memory port to read or -// write to an address. When a request is feasible, grant() returns -// True in the same cycle. -(* always_ready *) -interface MemArbiterWriter#(type addr); - method Action request(Bool write, addr address); +// A MemArbiterServer receives requests for memory access and emits +// grants. +interface MemArbiterServer#(type request); + method Action request(request req); method Bool grant(); endinterface -// MemArbiterReader can request use of a memory port to read from -// an address. When a request is feasible, grant() returns True in the -// same cycle. -(* always_ready *) -interface MemArbiterReader#(type addr); - method Action request(addr address); - method Bool grant(); +// A MemArbiterClient emits requests for memory access and emits +// grants. +interface MemArbiterClient#(type request); + method request request(); + method Action grant(); endinterface -// A MemArbiter manages concurrent access to memory ports. It -// mediates access between 2 writers and 4 readers. +typedef struct { + Bool write; + addr addr; +} MemArbiterWrite#(type addr) deriving (Bits, Eq); + +// A MemArbiter manages concurrent access to memory ports. interface MemArbiter#(type addr); - // Assigned to port A. - interface MemArbiterWriter#(addr) cpu; - interface MemArbiterWriter#(addr) debugger; - interface MemArbiterReader#(addr) palette; + interface MemArbiterServer#(MemArbiterWrite#(addr)) cpu; + interface MemArbiterServer#(MemArbiterWrite#(addr)) debugger; + interface MemArbiterServer#(addr) palette; - // Assigned to port B. - interface MemArbiterReader#(addr) tile1; - interface MemArbiterReader#(addr) tile2; - interface MemArbiterReader#(addr) sprite; + interface MemArbiterServer#(addr) tile1; + interface MemArbiterServer#(addr) tile2; + interface MemArbiterServer#(addr) sprite; endinterface // mkMemArbiter builds a GARY memory arbiter. // -// Port A arbitrates with strict priority: CPU requests always proceed -// first, followed by debugger requests, then the palette DAC. +// Port A arbitrates with strict priority: CPU requests go first, then +// the debugger, then the palette DAC. // // Port B does round-robin arbitration, giving each client a fair -// chance of having its requests processed. -module mkMemArbiter(MemArbiter#(addr) ifc) +// share of memory access. +module mkMemArbiter(MemArbiter#(addr)) provisos(Bits#(addr, _), Eq#(addr), - Alias#(write_req, Tuple2#(Bool, addr))); + Alias#(write_req, MemArbiterWrite#(addr))); ////// // Port A users @@ -73,16 +72,16 @@ module mkMemArbiter(MemArbiter#(addr) ifc) (* preempts = "grant_debugger, grant_palette" *) (* 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(); - if (write) - written_addr.wset(addr); + if (req.write) + written_addr.wset(req.addr); 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(); - if (write) - written_addr.wset(addr); + if (req.write) + written_addr.wset(req.addr); endrule rule grant_palette (palette_req); @@ -143,45 +142,35 @@ module mkMemArbiter(MemArbiter#(addr) ifc) ////// // External interface - interface MemArbiterWriter cpu; - method Action request(write, addr); - cpu_req.wset(tuple2(write, addr)); - endmethod + interface MemArbiterServer cpu; + method request = cpu_req.wset; method grant = cpu_ok; endinterface - interface MemArbiterWriter debugger; - method Action request(write, addr); - debugger_req.wset(tuple2(write, addr)); - endmethod + interface MemArbiterServer debugger; + method request = debugger_req.wset; method grant = debugger_ok; endinterface - interface MemArbiterReader palette; + interface MemArbiterServer palette; method Action request(addr); palette_req.send(); endmethod method grant = palette_ok; endinterface - interface MemArbiterReader tile1; - method Action request(addr); - portB_req[0].wset(addr); - endmethod + interface MemArbiterServer tile1; + method request = portB_req[0].wset; method grant = portB_grant[0]; endinterface - interface MemArbiterReader tile2; - method Action request(addr); - portB_req[1].wset(addr); - endmethod + interface MemArbiterServer tile2; + method request = portB_req[1].wset; method grant = portB_grant[1]; endinterface - interface MemArbiterReader sprite; - method Action request(addr); - portB_req[2].wset(addr); - endmethod + interface MemArbiterServer sprite; + method request = portB_req[2].wset; method grant = portB_grant[2]; endinterface endmodule diff --git a/vram/MemArbiter_Test.bsv b/vram/MemArbiter_Test.bsv index 9982c38..f415a5e 100644 --- a/vram/MemArbiter_Test.bsv +++ b/vram/MemArbiter_Test.bsv @@ -12,15 +12,10 @@ import MemArbiter::*; typedef UInt#(4) Addr; -typedef struct { - Bool write; - Addr addr; -} WriteReq deriving (Bits, Eq); - typedef struct { String name; - Maybe#(WriteReq) cpu; - Maybe#(WriteReq) debugger; + Maybe#(MemArbiterWrite#(Addr)) cpu; + Maybe#(MemArbiterWrite#(Addr)) debugger; Maybe#(Addr) palette; Maybe#(Addr) tile1; Maybe#(Addr) tile2; @@ -29,12 +24,12 @@ typedef struct { Vector#(6, Bool) want; } TestCase deriving (Bits, Eq); -function Maybe#(WriteReq) rwRead(Addr addr); - return tagged Valid WriteReq{write: False, addr: addr}; +function Maybe#(MemArbiterWrite#(Addr)) rwRead(Addr addr); + return tagged Valid MemArbiterWrite{write: False, addr: addr}; endfunction -function Maybe#(WriteReq) rwWrite(Addr addr); - return tagged Valid WriteReq{write: True, addr: addr}; +function Maybe#(MemArbiterWrite#(Addr)) rwWrite(Addr addr); + return tagged Valid MemArbiterWrite{write: True, addr: addr}; endfunction function Maybe#(Addr) read(Addr addr); @@ -55,8 +50,8 @@ function Vector#(6, Bool) grant(Integer granted_a, Integer granted_b); endfunction function TestCase testCase(String name, - Maybe#(WriteReq) cpu, - Maybe#(WriteReq) debugger, + Maybe#(MemArbiterWrite#(Addr)) cpu, + Maybe#(MemArbiterWrite#(Addr)) debugger, Maybe#(Addr) palette, Maybe#(Addr) tile1, Maybe#(Addr) tile2, @@ -209,13 +204,13 @@ module mkTB(); endrule (* no_implicit_conditions, fire_when_enabled *) - rule input_cpu (tests[idx].cpu matches tagged Valid .req &&& req matches WriteReq {write: .write, addr: .addr}); - dut.cpu.request(write, addr); + rule input_cpu (tests[idx].cpu matches tagged Valid .req); + dut.cpu.request(req); endrule (* no_implicit_conditions, fire_when_enabled *) - rule input_debugger (tests[idx].debugger matches tagged Valid .req &&& req matches WriteReq {write: .write, addr: .addr}); - dut.debugger.request(write, addr); + rule input_debugger (tests[idx].debugger matches tagged Valid .req); + dut.debugger.request(req); endrule (* no_implicit_conditions, fire_when_enabled *) @@ -238,7 +233,7 @@ module mkTB(); dut.sprite.request(addr); endrule - function Fmt rw_str(Maybe#(WriteReq) v); + function Fmt rw_str(Maybe#(MemArbiterWrite#(Addr)) v); case (v) matches tagged Valid .req: begin if (req.write)