301 lines
8.8 KiB
Plaintext
301 lines
8.8 KiB
Plaintext
package MemArbiter_Test;
|
|
|
|
import Assert::*;
|
|
import StmtFSM::*;
|
|
import Testing::*;
|
|
import Printf::*;
|
|
import List::*;
|
|
import Vector::*;
|
|
import BuildVector::*;
|
|
|
|
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#(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();
|
|
MemArbiter#(Addr) dut <- mkMemArbiter();
|
|
|
|
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 TestMemArbiter");
|
|
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 TestMemArbiter");
|
|
$finish;
|
|
end
|
|
else
|
|
idx <= next;
|
|
endrule
|
|
endmodule
|
|
|
|
endpackage
|