Projects sequence three phases instead of choosing one style. Directed proves life, constrained-random volume explores at scale, and targeted-random closes the holes that volume misses.
Phase | Goal | Currency |
|---|---|---|
Directed bring-up | TB and DUT alive | One write, one read, reset twice |
Random volume | Explore at scale | Broad constraints times many seeds |
Targeted closure | Close named holes | Narrowed constraints per hole |
Bug rate per cycle peaks in the volume phase. Effort per bug peaks in closure, where each hole is bespoke.
The next listing is not run in this course's checks. It is a fragment that uses bus_txn from the transaction lesson.
// not-run: fragment: bus_txn is defined in the transaction-design listing, no top module
class mixed_generator;
mailbox #(bus_txn) out;
bus_txn directed_q[$];
bus_txn blueprint;
int unsigned n;
event done;
function new(mailbox #(bus_txn) out, int unsigned n);
this.out = out; this.n = n; blueprint = new();
endfunction
function void add_directed(bit write, bit [15:0] addr, bit [31:0] wdata = '0);
bus_txn t = new();
t.kind = write ? WRITE : READ;
t.addr = addr;
t.wdata = wdata;
t.source = "directed";
t.rand_mode(0);
directed_q.push_back(t);
endfunction
task run();
int unsigned sent = 0;
while (directed_q.size() > 0) begin
out.put(directed_q.pop_front());
sent++;
end
while (sent < n) begin
if (!blueprint.randomize()) $fatal(1, "randomize failed");
out.put(blueprint.clone());
sent++;
end
-> done;
endtask
endclass
class hole_txn extends bus_txn;
constraint c_target {
addr >= 16'hF000;
gap == 0;
}
endclassDirected items drain first with rand_mode off, so no later solve perturbs the script. The driver never distinguishes sources because both emit one transaction class. Targeted-random pins only the hole neighbourhood and leaves all else random, so adjacent holes fall for free.
Unpinned fields keep exploring around the hole. A fully scripted test checks exactly one point and misses neighbours.
During bring-up the TB and DUT debug mix together. Directed life checks separate bench bugs from design bugs first.
A three-phase stand-in plus a testbench that checks order, volume, and targeting. A hole checker plays the role of c_target, and it must reject items outside the hole.
module tb_phases;
localparam int N_DIRECTED = 2;
localparam int N_VOLUME = 64;
localparam int N_HOLE = 8;
reg [15:0] directed_addr [0:1];
reg directed_write [0:1];
reg [15:0] lfsr;
reg [15:0] vol_addr [0:63];
reg [15:0] hole_addr [0:7];
reg [2:0] hole_gap [0:7];
integer i;
integer j;
integer problems;
// c_target as a checker: address in the hole window, back-to-back.
function automatic bit in_hole(input [15:0] a, input [2:0] g);
return (a >= 16'hF000) && (g == 0);
endfunction
initial begin
problems = 0;
// phase 1: directed write then read of one address
directed_write[0] = 1; directed_addr[0] = 16'h0000;
directed_write[1] = 0; directed_addr[1] = 16'h0000;
// phase 2: volume from an LFSR, which must not repeat in 64 steps
lfsr = 16'h0001;
for (i = 0; i < N_VOLUME; i = i + 1) begin
vol_addr[i] = lfsr;
lfsr = {lfsr[14:0], lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10]};
end
for (i = 0; i < N_VOLUME; i = i + 1)
for (j = i + 1; j < N_VOLUME; j = j + 1)
if (vol_addr[i] == vol_addr[j]) problems = problems + 1;
// phase 3: hole items pin the window and the gap, low bits still vary
for (i = 0; i < N_HOLE; i = i + 1) begin
hole_addr[i] = 16'hF000 | (vol_addr[i] & 16'h0FFF);
hole_gap[i] = 0;
if (!in_hole(hole_addr[i], hole_gap[i])) problems = problems + 1;
end
if (hole_addr[0] == hole_addr[N_HOLE-1]) problems = problems + 1;
// the checker must reject items outside the hole
if (in_hole(16'h1234, 3'd0)) problems = problems + 1;
if (in_hole(16'hF100, 3'd2)) problems = problems + 1;
if (problems == 0) $display("PASS: 2 directed first, 64 distinct volume, 8 back-to-back hole items");
else $fatal(1, "FAIL: found %0d phase problems", problems);
$finish;
end
endmodule