2024-08-31 22:25:04 +02:00
|
|
|
package MemArbiter_Test;
|
2024-08-31 22:25:04 +02:00
|
|
|
|
|
|
|
import Assert::*;
|
|
|
|
import StmtFSM::*;
|
|
|
|
import Testing::*;
|
|
|
|
import Printf::*;
|
|
|
|
import List::*;
|
|
|
|
import Vector::*;
|
|
|
|
import BuildVector::*;
|
|
|
|
|
2024-08-31 22:25:04 +02:00
|
|
|
import MemArbiter::*;
|
2024-08-31 22:25:04 +02:00
|
|
|
|
|
|
|
typedef UInt#(4) Addr;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
String name;
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs;
|
|
|
|
Maybe#(Addr) forbid_addr;
|
|
|
|
|
|
|
|
Vector#(n, Bool) want_grants;
|
|
|
|
Maybe#(Addr) want_forbid_addr;
|
|
|
|
} TestCase#(numeric type n) deriving (Bits, Eq);
|
|
|
|
|
|
|
|
function Maybe#(MemArbiterOp#(Addr)) read(Addr addr);
|
|
|
|
return tagged Valid MemArbiterOp{write: False, addr: addr};
|
2024-08-31 22:25:04 +02:00
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Maybe#(MemArbiterOp#(Addr)) write(Addr addr);
|
|
|
|
return tagged Valid MemArbiterOp{write: True, addr: addr};
|
2024-08-31 22:25:04 +02:00
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Maybe#(MemArbiterOp#(Addr)) idle();
|
|
|
|
return tagged Invalid;
|
2024-08-31 22:25:04 +02:00
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Maybe#(Addr) noForbid();
|
2024-08-31 22:25:04 +02:00
|
|
|
return tagged Invalid;
|
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Maybe#(Addr) forbid(Addr a);
|
|
|
|
return tagged Valid a;
|
|
|
|
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);
|
2024-08-31 22:25:04 +02:00
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function TestCase#(n) testCase(String name,
|
|
|
|
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs,
|
|
|
|
Maybe#(Addr) forbid_addr,
|
|
|
|
Vector#(n, Bool) want_grants,
|
|
|
|
Maybe#(Addr) want_forbid_addr);
|
2024-08-31 22:25:04 +02:00
|
|
|
return TestCase{
|
|
|
|
name: name,
|
2024-09-08 20:42:35 +02:00
|
|
|
reqs: reqs,
|
|
|
|
forbid_addr: forbid_addr,
|
|
|
|
want_grants: want_grants,
|
|
|
|
want_forbid_addr: want_forbid_addr
|
2024-08-31 22:25:04 +02:00
|
|
|
};
|
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
interface TB;
|
|
|
|
method Action start();
|
|
|
|
(* always_ready *)
|
|
|
|
method Bool done();
|
|
|
|
endinterface
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB ifc);
|
|
|
|
let cycles <- mkCycleCounter();
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
Reg#(Bit#(TLog#(m))) idx <- mkReg(0);
|
|
|
|
Reg#(Bool) running <- mkReg(False);
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
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
|
2024-08-31 22:25:04 +02:00
|
|
|
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
2024-09-08 20:42:35 +02:00
|
|
|
rule forbid (running && isValid(tests[idx].forbid_addr));
|
|
|
|
dut.forbid_addr(validValue(tests[idx].forbid_addr));
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
Wire#(Maybe#(Addr)) got_forbid_addr <- mkDWire(tagged Invalid);
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
(* fire_when_enabled *)
|
|
|
|
rule collect_forbid (running);
|
|
|
|
got_forbid_addr <= tagged Valid dut.forbidden_addr();
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Fmt req_s(Maybe#(MemArbiterOp#(Addr)) v);
|
2024-08-31 22:25:04 +02:00
|
|
|
case (v) matches
|
2024-09-08 20:42:35 +02:00
|
|
|
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);
|
2024-08-31 22:25:04 +02:00
|
|
|
endcase
|
|
|
|
endfunction
|
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
function Fmt addr_s(Maybe#(Addr) v);
|
2024-08-31 22:25:04 +02:00
|
|
|
case (v) matches
|
2024-09-08 20:42:35 +02:00
|
|
|
tagged Invalid: return $format("<nil>");
|
|
|
|
tagged Valid .a: return $format("%0d", a);
|
2024-08-31 22:25:04 +02:00
|
|
|
endcase
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
2024-09-08 20:42:35 +02:00
|
|
|
rule check (running);
|
2024-08-31 22:25:04 +02:00
|
|
|
let test = tests[idx];
|
2024-09-08 20:42:35 +02:00
|
|
|
let reqs = test.reqs;
|
|
|
|
let want_grants = test.want_grants;
|
|
|
|
let want_forbid_addr = test.want_forbid_addr;
|
|
|
|
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_forbid_addr != want_forbid_addr) begin
|
|
|
|
$display(" input:");
|
|
|
|
for (Integer i=0; i<valueOf(n); i=i+1)
|
|
|
|
$display(" ", $format("%0d", i), ": ", req_s(reqs[i]));
|
|
|
|
$display(" forbid: ", addr_s(test.forbid_addr));
|
|
|
|
|
|
|
|
$display(" output:");
|
|
|
|
$display(" grants: ", fshow(got_grants));
|
|
|
|
$display(" forbid: ", addr_s(got_forbid_addr));
|
|
|
|
|
|
|
|
$display(" want grants: ", fshow(tests[idx].want_grants));
|
|
|
|
$display(" want forbid: ", addr_s(want_forbid_addr));
|
|
|
|
dynamicAssert(False, "wrong arbiter output");
|
2024-08-31 22:25:04 +02:00
|
|
|
end
|
2024-08-31 22:25:04 +02:00
|
|
|
|
2024-09-08 20:42:35 +02:00
|
|
|
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;
|
2024-08-31 22:25:04 +02:00
|
|
|
else
|
2024-09-08 20:42:35 +02:00
|
|
|
idx <= idx+1;
|
2024-08-31 22:25:04 +02:00
|
|
|
endrule
|
2024-09-08 20:42:35 +02:00
|
|
|
|
|
|
|
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), noForbid,
|
|
|
|
noGrant, noForbid),
|
|
|
|
testCase("Port 0 read",
|
|
|
|
vec(read(1), idle, idle), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0 write",
|
|
|
|
vec(write(1), idle, idle), noForbid,
|
|
|
|
grant(0), forbid(1)),
|
|
|
|
testCase("Port 1 read",
|
|
|
|
vec(idle, read(1), idle), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 1 write",
|
|
|
|
vec(idle, write(1), idle), noForbid,
|
|
|
|
grant(1), forbid(1)),
|
|
|
|
testCase("Port 2 read",
|
|
|
|
vec(idle, idle, read(1)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("Port 2 write",
|
|
|
|
vec(idle, idle, write(1)), noForbid,
|
|
|
|
grant(2), forbid(1)),
|
|
|
|
|
|
|
|
// Priorities
|
|
|
|
testCase("Port 0+1",
|
|
|
|
vec(read(1), read(2), idle), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+2",
|
|
|
|
vec(read(1), idle, read(2)), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 1+2",
|
|
|
|
vec(idle, read(1), read(2)), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 0+1+2",
|
|
|
|
vec(read(1), read(2), read(3)), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+1+2, overruled writes",
|
|
|
|
vec(read(1), write(1), write(2)), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
|
|
|
|
// Forbidden addrs
|
|
|
|
testCase("Port 0 read denied",
|
|
|
|
vec(read(1), read(2), idle), forbid(1),
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 0 write denied",
|
|
|
|
vec(write(1), read(2), idle), forbid(1),
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 0 no addr match",
|
|
|
|
vec(write(2), idle, idle), forbid(1),
|
|
|
|
grant(0), forbid(2)),
|
|
|
|
testCase("Port 0 denied, no alternatives",
|
|
|
|
vec(write(1), idle, idle), forbid(1),
|
|
|
|
noGrant, noForbid)
|
|
|
|
);
|
|
|
|
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), noForbid,
|
|
|
|
noGrant, noForbid),
|
|
|
|
testCase("Port 0 read",
|
|
|
|
vec(read(1), idle, idle), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0 write",
|
|
|
|
vec(write(1), idle, idle), noForbid,
|
|
|
|
grant(0), forbid(1)),
|
|
|
|
testCase("Port 1 read",
|
|
|
|
vec(idle, read(1), idle), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 1 write",
|
|
|
|
vec(idle, write(1), idle), noForbid,
|
|
|
|
grant(1), forbid(1)),
|
|
|
|
testCase("Port 2 read",
|
|
|
|
vec(idle, idle, read(1)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("Port 2 write",
|
|
|
|
vec(idle, idle, write(1)), noForbid,
|
|
|
|
grant(2), forbid(1)),
|
|
|
|
|
|
|
|
// Priorities
|
|
|
|
testCase("Port 2 to reset RR",
|
|
|
|
vec(idle, idle, read(1)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("Port 0+1 #1",
|
|
|
|
vec(read(1), read(2), idle), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+1 #2",
|
|
|
|
vec(read(1), read(2), idle), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 0+1 #3",
|
|
|
|
vec(read(1), read(2), idle), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+2 #1",
|
|
|
|
vec(read(1), idle, read(2)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("Port 0+2 #2",
|
|
|
|
vec(read(1), idle, read(2)), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+1+2 #1",
|
|
|
|
vec(read(1), read(2), read(3)), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
testCase("Port 0+1+2 #2",
|
|
|
|
vec(read(1), read(2), read(3)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("Port 0+1+2 #3",
|
|
|
|
vec(read(1), read(2), read(3)), noForbid,
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("Port 0+1+2 #4",
|
|
|
|
vec(read(1), read(2), read(3)), noForbid,
|
|
|
|
grant(1), noForbid),
|
|
|
|
|
|
|
|
// Forbidden addrs
|
|
|
|
testCase("Port 2 to reset RR",
|
|
|
|
vec(idle, idle, read(1)), noForbid,
|
|
|
|
grant(2), noForbid),
|
|
|
|
testCase("RR with denied writes #1",
|
|
|
|
vec(read(1), write(2), read(3)), forbid(3),
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("RR with denied writes #2",
|
|
|
|
vec(read(1), write(2), read(3)), forbid(3),
|
|
|
|
grant(1), forbid(2)),
|
|
|
|
testCase("RR with denied writes #3",
|
|
|
|
vec(read(1), write(2), read(3)), forbid(3),
|
|
|
|
grant(0), noForbid),
|
|
|
|
testCase("RR with denied writes #4",
|
|
|
|
vec(read(1), write(2), read(3)), forbid(3),
|
|
|
|
grant(1), forbid(2))
|
|
|
|
);
|
|
|
|
MemArbiter#(3, 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));
|
2024-08-31 22:25:04 +02:00
|
|
|
endmodule
|
|
|
|
|
|
|
|
endpackage
|