321 lines
10 KiB
Plaintext
321 lines
10 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 {
|
|
String name;
|
|
|
|
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs;
|
|
Maybe#(MemArbiterOp#(Addr)) conflict;
|
|
|
|
Vector#(n, Bool) want_grants;
|
|
Maybe#(MemArbiterOp#(Addr)) want_granted_op;
|
|
} TestCase#(numeric type n) deriving (Bits, Eq);
|
|
|
|
function Maybe#(MemArbiterOp#(Addr)) read(Addr addr);
|
|
return tagged Valid MemArbiterOp{write: False, addr: addr};
|
|
endfunction
|
|
|
|
function Maybe#(MemArbiterOp#(Addr)) write(Addr addr);
|
|
return tagged Valid MemArbiterOp{write: True, addr: addr};
|
|
endfunction
|
|
|
|
function Maybe#(MemArbiterOp#(Addr)) idle();
|
|
return tagged Invalid;
|
|
endfunction
|
|
|
|
function Maybe#(MemArbiterOp#(Addr)) noConflict();
|
|
return tagged Invalid;
|
|
endfunction
|
|
|
|
function Vector#(n, Bool) grant(Integer granted);
|
|
function gen(idx);
|
|
return idx == granted;
|
|
endfunction
|
|
|
|
return genWith(gen);
|
|
endfunction
|
|
|
|
function Vector#(n, Bool) noGrant();
|
|
return replicate(False);
|
|
endfunction
|
|
|
|
function TestCase#(n) testCase(String name,
|
|
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs,
|
|
Maybe#(MemArbiterOp#(Addr)) conflict,
|
|
Vector#(n, Bool) want_grants,
|
|
Maybe#(MemArbiterOp#(Addr)) want_granted_op);
|
|
return TestCase{
|
|
name: name,
|
|
reqs: reqs,
|
|
conflict: conflict,
|
|
want_grants: want_grants,
|
|
want_granted_op: want_granted_op
|
|
};
|
|
endfunction
|
|
|
|
interface TB;
|
|
method Action start();
|
|
(* always_ready *)
|
|
method Bool done();
|
|
endinterface
|
|
|
|
module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB ifc);
|
|
let cycles <- mkCycleCounter();
|
|
|
|
Reg#(Bit#(TLog#(m))) idx <- mkReg(0);
|
|
Reg#(Bool) running <- mkReg(False);
|
|
|
|
for (Integer i=0; i<valueOf(n); i=i+1) begin
|
|
rule request (running && isValid(tests[idx].reqs[i]));
|
|
dut.ports[i].request(validValue(tests[idx].reqs[i]));
|
|
endrule
|
|
end
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule forbid (running && isValid(tests[idx].conflict));
|
|
dut.conflict(validValue(tests[idx].conflict));
|
|
endrule
|
|
|
|
Wire#(Maybe#(MemArbiterOp#(Addr))) got_granted_op <- mkDWire(tagged Invalid);
|
|
|
|
(* fire_when_enabled *)
|
|
rule collect_granted_op (running);
|
|
got_granted_op <= tagged Valid dut.granted_op();
|
|
endrule
|
|
|
|
function Fmt req_s(Maybe#(MemArbiterOp#(Addr)) v);
|
|
case (v) matches
|
|
tagged Invalid: return $format("Idle");
|
|
tagged Valid .req &&& req.write: return $format("Write(%0d)", req.addr);
|
|
tagged Valid .req: return $format("Read(%0d)", req.addr);
|
|
endcase
|
|
endfunction
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule check (running);
|
|
let test = tests[idx];
|
|
let reqs = test.reqs;
|
|
let want_grants = test.want_grants;
|
|
let want_granted_op = test.want_granted_op;
|
|
Vector#(n, Bool) got_grants = newVector;
|
|
for (Integer i=0; i<valueOf(n); i=i+1)
|
|
got_grants[i] = dut.ports[i].grant();
|
|
|
|
$display("RUN %s (%0d)", tests[idx].name, idx);
|
|
if (got_grants != want_grants || got_granted_op != want_granted_op) begin
|
|
$display("input:");
|
|
for (Integer i=0; i<valueOf(n); i=i+1)
|
|
$display(" ", $format("%0d", i), ": ", req_s(reqs[i]));
|
|
$display(" conflict: ", fshow(test.conflict));
|
|
|
|
$display(" output:");
|
|
$display(" grants: ", fshow(got_grants));
|
|
$display(" granted: ", fshow(got_granted_op));
|
|
|
|
$display(" want grants: ", fshow(tests[idx].want_grants));
|
|
$display(" want granted: ", fshow(want_granted_op));
|
|
dynamicAssert(False, "wrong arbiter output");
|
|
end
|
|
|
|
dynamicAssert(cycles == 1, "arbiter took more than 0 cycles");
|
|
|
|
$display("OK %s", tests[idx].name);
|
|
|
|
cycles.reset();
|
|
if (idx == fromInteger(valueOf(m)-1))
|
|
running <= False;
|
|
else
|
|
idx <= idx+1;
|
|
endrule
|
|
|
|
method Action start() if (!running && idx == 0);
|
|
cycles.reset();
|
|
running <= True;
|
|
endmethod
|
|
|
|
method Bool done();
|
|
return !running && idx != 0;
|
|
endmethod
|
|
endmodule
|
|
|
|
module mkTB(Empty);
|
|
///////////////////////////////
|
|
// Strict arbiter
|
|
|
|
let strictTests = vec(
|
|
// Simple grants
|
|
testCase("All idle",
|
|
vec(idle, idle, idle), noConflict,
|
|
noGrant, noConflict),
|
|
testCase("Port 0 read",
|
|
vec(read(1), idle, idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0 write",
|
|
vec(write(1), idle, idle), noConflict,
|
|
grant(0), write(1)),
|
|
testCase("Port 1 read",
|
|
vec(idle, read(1), idle), noConflict,
|
|
grant(1), read(1)),
|
|
testCase("Port 1 write",
|
|
vec(idle, write(1), idle), noConflict,
|
|
grant(1), write(1)),
|
|
testCase("Port 2 read",
|
|
vec(idle, idle, read(1)), noConflict,
|
|
grant(2), read(1)),
|
|
testCase("Port 2 write",
|
|
vec(idle, idle, write(1)), noConflict,
|
|
grant(2), write(1)),
|
|
|
|
// Priorities
|
|
testCase("Port 0+1",
|
|
vec(read(1), read(2), idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+2",
|
|
vec(read(1), idle, read(2)), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 1+2",
|
|
vec(idle, read(1), read(2)), noConflict,
|
|
grant(1), read(1)),
|
|
testCase("Port 0+1+2",
|
|
vec(read(1), read(2), read(3)), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+1+2, overruled writes",
|
|
vec(read(1), write(1), write(2)), noConflict,
|
|
grant(0), read(1)),
|
|
|
|
// Forbidden addrs
|
|
testCase("Port 0 read-write denied",
|
|
vec(read(1), read(2), idle), write(1),
|
|
grant(1), read(2)),
|
|
testCase("Port 0 write-write denied",
|
|
vec(write(1), read(2), idle), write(1),
|
|
grant(1), read(2)),
|
|
testCase("Port 0 write-read denied",
|
|
vec(write(1), read(2), idle), read(1),
|
|
grant(1), read(2)),
|
|
testCase("Port 0 no addr match",
|
|
vec(write(2), idle, idle), write(1),
|
|
grant(0), write(2)),
|
|
testCase("Port 0 denied, no alternatives",
|
|
vec(write(1), idle, idle), write(1),
|
|
noGrant, noConflict)
|
|
);
|
|
MemArbiter#(3, Addr) strict <- mkPriorityMemArbiter();
|
|
let strictTB <- mkArbiterTB(strict, strictTests);
|
|
|
|
///////////////////////////////
|
|
// Round-robin arbiter
|
|
let rrTests = vec(
|
|
// Simple grants
|
|
testCase("All idle",
|
|
vec(idle, idle, idle, idle), noConflict,
|
|
noGrant, noConflict),
|
|
testCase("Port 0 read",
|
|
vec(read(1), idle, idle, idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0 write",
|
|
vec(write(1), idle, idle, idle), noConflict,
|
|
grant(0), write(1)),
|
|
testCase("Port 1 read",
|
|
vec(idle, read(1), idle, idle), noConflict,
|
|
grant(1), read(1)),
|
|
testCase("Port 1 write",
|
|
vec(idle, write(1), idle, idle), noConflict,
|
|
grant(1), write(1)),
|
|
testCase("Port 2 read",
|
|
vec(idle, idle, read(1), idle), noConflict,
|
|
grant(2), read(1)),
|
|
testCase("Port 2 write",
|
|
vec(idle, idle, write(1), idle), noConflict,
|
|
grant(2), write(1)),
|
|
testCase("Port 3 read",
|
|
vec(idle, idle, idle, read(1)), noConflict,
|
|
grant(3), read(1)),
|
|
testCase("Port 3 write",
|
|
vec(idle, idle, idle, write(1)), noConflict,
|
|
grant(3), write(1)),
|
|
|
|
// Priorities
|
|
testCase("Port 3 to reset RR",
|
|
vec(idle, idle, idle, read(1)), noConflict,
|
|
grant(3), read(1)),
|
|
testCase("Port 0+1 #1",
|
|
vec(read(1), read(2), idle, idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+1 #2",
|
|
vec(read(1), read(2), idle, idle), noConflict,
|
|
grant(1), read(2)),
|
|
testCase("Port 0+1 #3",
|
|
vec(read(1), read(2), idle, idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+2 #1",
|
|
vec(read(1), idle, read(2), idle), noConflict,
|
|
grant(2), read(2)),
|
|
testCase("Port 0+2 #2",
|
|
vec(read(1), idle, read(2), idle), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+1+2+3 #1",
|
|
vec(read(1), read(2), read(3), read(4)), noConflict,
|
|
grant(1), read(2)),
|
|
testCase("Port 0+1+2+3 #2",
|
|
vec(read(1), read(2), read(3), read(4)), noConflict,
|
|
grant(2), read(3)),
|
|
testCase("Port 0+1+2+3 #3",
|
|
vec(read(1), read(2), read(3), read(4)), noConflict,
|
|
grant(3), read(4)),
|
|
testCase("Port 0+1+2+3 #4",
|
|
vec(read(1), read(2), read(3), read(4)), noConflict,
|
|
grant(0), read(1)),
|
|
testCase("Port 0+1+2+3 #5",
|
|
vec(read(1), read(2), read(3), read(4)), noConflict,
|
|
grant(1), read(2)),
|
|
|
|
// Forbidden addrs
|
|
testCase("Port 3 to reset RR",
|
|
vec(idle, idle, idle, read(1)), noConflict,
|
|
grant(3), read(1)),
|
|
testCase("RR with denied writes #1",
|
|
vec(read(1), write(2), read(3), read(4)), write(3),
|
|
grant(0), read(1)),
|
|
testCase("RR with denied writes #2",
|
|
vec(read(1), write(2), read(3), read(4)), write(3),
|
|
grant(1), write(2)),
|
|
testCase("RR with denied writes #3",
|
|
vec(read(1), write(2), read(3), read(4)), write(3),
|
|
grant(3), read(4)),
|
|
testCase("RR with denied writes #4",
|
|
vec(read(1), write(2), read(3), read(4)), write(3),
|
|
grant(0), read(1)),
|
|
testCase("RR with denied writes #5",
|
|
vec(read(1), write(2), read(3), read(4)), write(3),
|
|
grant(1), write(2))
|
|
);
|
|
MemArbiter#(4, Addr) rr <- mkRoundRobinMemArbiter();
|
|
let rrTB <- mkArbiterTB(rr, rrTests);
|
|
|
|
runTest(100,
|
|
mkTest("MemArbiter", seq
|
|
mkTest("MemArbiter/Strict", seq
|
|
strictTB.start();
|
|
await(strictTB.done);
|
|
endseq);
|
|
mkTest("MemArbiter/RoundRobin", seq
|
|
rrTB.start();
|
|
await(rrTB.done);
|
|
endseq);
|
|
endseq));
|
|
endmodule
|
|
|
|
endpackage
|