package MemoryArbiter_Test; import Assert::*; import StmtFSM::*; import Testing::*; import Printf::*; import List::*; import Vector::*; import BuildVector::*; import MemoryArbiter::*; 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#(Addr) palette; Maybe#(Addr) tile1; Maybe#(Addr) tile2; Maybe#(Addr) sprite; Vector#(6, Bool) want; } TestCase deriving (Bits, Eq); function Maybe#(WriteReq) rwRead(Addr addr); return tagged Valid WriteReq{write: False, addr: addr}; endfunction function Maybe#(WriteReq) rwWrite(Addr addr); return tagged Valid WriteReq{write: True, addr: addr}; endfunction function Maybe#(Addr) read(Addr addr); return tagged Valid addr; endfunction function Maybe#(t) idle(); return tagged Invalid; endfunction function Vector#(6, Bool) grant(Integer granted_a, Integer granted_b); let ret = replicate(False); if (granted_a >= 0) ret[granted_a] = True; if (granted_b >= 0) ret[granted_b+3] = True; return ret; endfunction function TestCase testCase(String name, Maybe#(WriteReq) cpu, Maybe#(WriteReq) debugger, Maybe#(Addr) palette, Maybe#(Addr) tile1, Maybe#(Addr) tile2, Maybe#(Addr) sprite, Integer portA, Integer portB); return TestCase{ name: name, cpu: cpu, debugger: debugger, palette: palette, tile1: tile1, tile2: tile2, sprite: sprite, want: grant(portA, portB) }; endfunction module mkTB(); MemoryArbiter#(Addr) dut <- mkMemoryArbiter(); Vector#(29, TestCase) tests = vec( testCase("All idle", idle, idle, idle, idle, idle, idle, -1, -1), // Single client accesses at a time testCase("CPU read", rwRead(1), idle, idle, idle, idle, idle, 0, -1), testCase("CPU write", rwWrite(1), idle, idle, idle, idle, idle, 0, -1), testCase("Debugger read", idle, rwRead(1), idle, idle, idle, idle, 1, -1), testCase("Debugger write", idle, rwWrite(1), idle, idle, idle, idle, 1, -1), testCase("Palette read", idle, idle, read(1), idle, idle, idle, 2, -1), testCase("Tile1 read", idle, idle, idle, read(1), idle, idle, -1, 0), testCase("Tile2 read", idle, idle, idle, idle, read(1), idle, -1, 1), testCase("Sprite read", idle, idle, idle, idle, idle, read(1), -1, 2), // Strict priority on port A testCase("CPU + Debugger + Palette", rwRead(1), rwRead(2), read(3), idle, idle, idle, 0, -1), testCase("CPU + Palette", rwRead(1), idle, read(3), idle, idle, idle, 0, -1), testCase("Debugger + Palette", idle, rwRead(2), read(3), idle, idle, idle, 1, -1), // Round-robin on port B testCase("Sprite read", // to reset round robin idle, idle, idle, idle, idle, read(1), -1, 2), testCase("Tile1 + Tile2 + Sprite", idle, idle, idle, read(1), read(2), read(3), -1, 0), testCase("Tile1 + Tile2 + Sprite", idle, idle, idle, read(1), read(2), read(3), -1, 1), testCase("Tile1 + Tile2 + Sprite", idle, idle, idle, read(1), read(2), read(3), -1, 2), testCase("Tile1 + Tile2 + Sprite", idle, idle, idle, read(1), read(2), read(3), -1, 0), testCase("Tile1 + Sprite", idle, idle, idle, read(1), idle, read(3), -1, 2), testCase("Tile2 + Sprite", idle, idle, idle, idle, read(2), read(3), -1, 1), testCase("Tile2 + Sprite", idle, idle, idle, idle, read(2), read(3), -1, 2), // Inter-port conflicts testCase("Read/read, no conflict", rwRead(0), idle, idle, read(0), idle, idle, 0, 0), testCase("Write/read, no conflict", rwWrite(1), idle, idle, read(0), idle, idle, 0, 0), testCase("Tile1 write conflict", rwWrite(0), idle, idle, read(0), idle, idle, 0, -1), testCase("Tile2 write conflict", rwWrite(0), idle, idle, idle, read(0), idle, 0, -1), testCase("Sprite write conflict", rwWrite(0), idle, idle, idle, idle, read(0), 0, -1), testCase("Tile1 write conflict with debugger", idle, rwWrite(0), idle, read(0), idle, idle, 1, -1), testCase("Sprite read", // to reset round robin idle, idle, idle, idle, idle, read(1), -1, 2), testCase("CPU write conflict, other port feasible", rwWrite(0), idle, idle, read(0), read(1), idle, 0, 1), testCase("CPU write conflict, conflict resolved", idle, idle, idle, read(0), idle, idle, -1, 0)); Reg#(UInt#(32)) idx <- mkReg(0); rule display_test (idx == 0); $display("RUN TestMemoryArbiter"); 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); 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); endrule (* no_implicit_conditions, fire_when_enabled *) rule input_palette (tests[idx].palette matches tagged Valid .addr); dut.palette.request(addr); endrule (* no_implicit_conditions, fire_when_enabled *) rule input_tile1 (tests[idx].tile1 matches tagged Valid .addr); dut.tile1.request(addr); endrule (* no_implicit_conditions, fire_when_enabled *) rule input_tile2 (tests[idx].tile2 matches tagged Valid .addr); dut.tile2.request(addr); endrule (* no_implicit_conditions, fire_when_enabled *) rule input_sprite (tests[idx].sprite matches tagged Valid .addr); dut.sprite.request(addr); endrule function Fmt rw_str(Maybe#(WriteReq) v); case (v) matches tagged Valid .req: begin if (req.write) return $format("Write(%0d)", req.addr); else return $format("Read(%0d) ", req.addr); end tagged Invalid: return $format("Idle "); endcase endfunction function Fmt ro_str(Maybe#(Addr) v); case (v) matches tagged Valid .addr: return $format("Read(%0d) ", addr); tagged Invalid: return $format("Idle "); endcase endfunction (* no_implicit_conditions, fire_when_enabled *) rule check_grants; Vector#(6, Bool) gotVec = newVector; gotVec[0] = dut.cpu.grant(); gotVec[1] = dut.debugger.grant(); gotVec[2] = dut.palette.grant(); gotVec[3] = dut.tile1.grant(); gotVec[4] = dut.tile2.grant(); gotVec[5] = dut.sprite.grant(); let test = tests[idx]; let got = pack(reverse(gotVec)); let want = pack(reverse(test.want)); $display("RUN %s (%0d)", test.name, idx+1); if (got != want) begin $display(" input: ", "0:", rw_str(test.cpu), " 1:", rw_str(test.debugger), " 2:", ro_str(test.palette), " 3:", ro_str(test.tile1), " 4:", ro_str(test.tile2), " 5:", ro_str(test.sprite)); $display(" got : %03b %03b", got[5:3], got[2:0]); $display(" want : %03b %03b", want[5:3], want[2:0]); dynamicAssert(got == want, "wrong arbiter output"); end else $display("OK %s", test.name); endrule (* no_implicit_conditions, fire_when_enabled *) rule advance_test; let next = idx+1; let max = fromInteger(arrayLength(vectorToArray(tests))); if (next == max) begin $display("OK TestMemoryArbiter"); $finish; end else idx <= next; endrule endmodule endpackage