gary/vram/MemoryArbiter_Test.bsv

301 lines
8.8 KiB
Plaintext
Raw Normal View History

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,
2024-08-31 22:25:04 +02:00
-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