The simplest correct generator randomizes a blueprint object in a loop and puts a clone into the mailbox. Tests retarget the stream by swapping the blueprint for a derived object, with zero generator edits. This relies on clone() being virtual: a derived blueprint that adds fields overrides it, so every queued copy keeps the derived type.
Piece | Role | Rule |
|---|---|---|
Blueprint | Template transaction | Tests may replace it with a derived type |
Clone on put | Handle safety | Every queued item is a fresh object |
Solve check | Robustness | Every call checks its return and stops loudly |
Mix knob | Scenario share | One dist over scenario kinds |
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 generator;
mailbox #(bus_txn) out;
int unsigned n;
bus_txn blueprint;
event done;
function new(mailbox #(bus_txn) out, int unsigned n);
this.out = out; this.n = n;
blueprint = new();
endfunction
virtual task run();
repeat (n) begin
if (!blueprint.randomize())
$fatal(1, "GEN: randomize failed");
out.put(blueprint.clone());
end
-> done;
endtask
endclass
class scenario_gen extends generator;
typedef enum {SINGLE, WR_RD_BACK, BURST4} scen_e;
rand scen_e scen;
constraint c_mix { scen dist {SINGLE :/ 50, WR_RD_BACK :/ 30, BURST4 :/ 20}; }
virtual task run();
int unsigned sent = 0;
int unsigned got = 0;
while (sent < n) begin
if (!this.randomize()) $fatal(1, "GEN: scen pick failed");
case (scen)
SINGLE: begin do_single(got); sent += got; end
WR_RD_BACK: begin do_wr_rd_back(got); sent += got; end
BURST4: begin do_burst4(got); sent += got; end
endcase
end
-> done;
endtask
task automatic do_single(output int unsigned added);
bus_txn c;
if (!blueprint.randomize()) $fatal(1, "GEN: single failed");
c = blueprint.clone();
out.put(c);
added = 1;
endtask
task automatic do_wr_rd_back(output int unsigned added);
bus_txn wr, rd;
if (!blueprint.randomize() with { kind == WRITE; })
$fatal(1, "GEN: wr failed");
wr = blueprint.clone();
rd = wr.clone();
rd.kind = READ;
out.put(wr); out.put(rd);
added = 2;
endtask
task automatic do_burst4(output int unsigned added);
int unsigned k;
if (!blueprint.randomize() with { kind == WRITE; addr[3:0] == 0; })
$fatal(1, "GEN: burst failed");
for (k = 0; k < 4; k++) begin
bus_txn b = blueprint.clone();
b.addr = blueprint.addr + 4 * k;
b.gap = 0;
out.put(b);
end
added = 4;
endtask
endclassScenario helpers are tasks because mailbox put blocks, and functions cannot block. Each path checks its solve result like the baseline. Solver-side correlation across items belongs to scenario classes, covered in the previous lesson; these tasks build procedural patterns item by item.
Write-readback shares the address by clone-and-edit, so the pair checks something by construction.
Bursts force intra-scenario gap to zero while the driver still obeys the protocol.
Stopping uses count, coverage through $get_coverage, or an event, with a max-count safety cap.
They call mailbox put, which blocks. Functions cannot call tasks or block, so the helpers take task form with an output count.
Nothing in the generator: the test swaps in a derived blueprint with the extra constraint. That swap is the hand-rolled factory override.
A deterministic mix model plus a testbench that checks the 190-item composition. The stand-in fixes the 50/30/20 split exactly; a real dist only approaches it.
module tb_mix;
localparam int N_PICK = 100;
// Scenario kind for pick slot 0..9 (a fixed 50/30/20 split), and
// the number of items each kind puts into the mailbox.
function automatic int kind_of(int slot);
if (slot < 5) return 0; // SINGLE
else if (slot < 8) return 1; // WR_RD_BACK
else return 2; // BURST4
endfunction
function automatic int items_of(int kind);
case (kind)
0: return 1;
1: return 2;
default: return 4;
endcase
endfunction
integer i;
integer k;
integer singles;
integer pairs;
integer bursts;
integer sent;
integer problems;
initial begin
singles = 0; pairs = 0; bursts = 0; sent = 0; problems = 0;
for (i = 0; i < N_PICK; i = i + 1) begin
k = kind_of(i % 10);
sent = sent + items_of(k);
if (k == 0) singles = singles + 1;
else if (k == 1) pairs = pairs + 1;
else bursts = bursts + 1;
end
if (singles != 50) problems = problems + 1;
if (pairs != 30) problems = problems + 1;
if (bursts != 20) problems = problems + 1;
if (sent != 190) problems = problems + 1;
if (problems == 0) $display("PASS: 190 items from 50 singles, 30 pairs, 20 bursts");
else $fatal(1, "FAIL: found %0d composition problems", problems);
$finish;
end
endmodule