obj.randomize() with { ... } adds expressions for one call only. The solver ANDs them with the active class blocks, so inline code can only shrink the solution space.
class bus_txn;
rand bit [31:0] addr;
rand bit [7:0] len;
constraint legal_c { len inside {[1:16]}; addr < 32'h1_0000; }
endclass
module tb_tighten_only;
initial begin
automatic bus_txn t = new();
// Tighten: legal AND call-site, so len in [4:16], addr in [0x8000:0xFFFF]
if (!t.randomize() with { len >= 4; addr >= 32'h8000; })
$fatal(1, "FAIL: the tightened solve failed");
if (t.len < 4 || t.len > 16 || t.addr < 32'h8000 || t.addr > 32'hFFFF)
$fatal(1, "FAIL: len=%0d addr=%h outside the intersection", t.len, t.addr);
// Contradiction: len==32 meets len in [1:16] nowhere
if (!t.randomize() with { len == 32; })
$display("empty intersection: no value satisfies both rules");
else $fatal(1, "FAIL: len == 32 should contradict legal_c");
$display("PASS: inline with only tightened the class rules");
$finish;
end
endmodule%Warning-UNSATCONSTR: /var/folders/41/dgs80y1569b4x8pwflv_w9jr0000gn/T/rtbtb-K1WcxI/t20.sv:4: Unsatisfied constraint: 'constraint legal_c { len inside {[1:16]}; addr < 32'h1_0000; }'
%Warning-UNSATCONSTR: /var/folders/41/dgs80y1569b4x8pwflv_w9jr0000gn/T/rtbtb-K1WcxI/t20.sv:15: Unsatisfied constraint: 'if (!t.randomize() with { len == 32; })'
empty intersection: no value satisfies both rules
PASS: inline with only tightened the class rulesA failed randomize() returns 0 and leaves every rand field holding its prior value (IEEE 1800 18.6.3). Driving the stale object is the follow-on bug, so always check the return.
Names inside with resolve against the randomized object first, then the enclosing scope. A sequence variable that shadows a field therefore binds to the field on both sides.
addr = 32'h4000 and calls t.randomize() with { addr == addr; }. What value does t.addr get?Any legal value. Both names bind to t.addr, so the rule is t.addr == t.addr, always true. local::addr is the fix.
class bus_txn;
rand bit [31:0] addr;
rand bit [7:0] len;
constraint legal_c { len inside {[1:16]}; addr < 32'h1_0000; }
endclass
class my_seq;
bit [31:0] addr; // the sequence's own addr shadows the field
task body(bus_txn t);
addr = 32'h4000;
// Bug: both names bind to t.addr, so the rule is t.addr == t.addr
void'(t.randomize() with { addr == addr; });
// Fix: local:: skips the object and binds the calling scope
void'(t.randomize() with { addr == local::addr; });
if (t.addr != 32'h4000)
$fatal(1, "FAIL: local::addr did not bind, addr=%h", t.addr);
endtask
endclass
module tb_scope;
initial begin
automatic my_seq s = new();
automatic bus_txn t = new();
s.body(t);
$display("PASS: local::addr pinned t.addr to 4000");
$finish;
end
endmodulePASS: local::addr pinned t.addr to 4000Written in the with block | Binds to | Result |
|---|---|---|
| The object's own field |
|
| The calling scope's |
|
The tautology never fails, so nothing points at it. The symptom arrives late as coverage holes or scoreboard noise.
Inline wins for one-off call-local intent: this loop wants short lengths, this call pins one address. Reusable personalities belong in subclasses, and relaxation is impossible inline.
class bus_txn;
rand bit [31:0] addr;
rand bit [7:0] len;
constraint legal_c { len inside {[1:16]}; addr < 32'h1_0000; }
endclass
class short_bus_txn extends bus_txn;
constraint short_c { len inside {[1:4]}; } // still ANDs with legal_c
endclass
module tb_one_off;
bit [31:0] targets[]; // directed sweep values, sized by the test
initial begin
automatic bus_txn t = new();
targets = new[4];
foreach (targets[i]) begin
// Inline: a directed sweep inside one sequence
void'(t.randomize() with { addr == local::targets[i]; len == 1; });
if (t.addr != targets[i] || t.len != 1)
$fatal(1, "FAIL: sweep %0d gave addr=%h len=%0d", i, t.addr, t.len);
end
// Inline CANNOT ask for len > 16: that contradicts legal_c,
// so relaxation needs constraint_mode or a subclass.
$display("PASS: 4 directed sweep calls hit their targets");
$finish;
end
endmodulePASS: 4 directed sweep calls hit their targetsNo. They intersect with every active class constraint for exactly one call. The space can only shrink, apart from hard inline expressions displacing class-level soft constraints.
It returns 0 and leaves all rand fields at their prior values. Treat the 0 as a coding fault, not a retry, or the bench will drive a stale transaction.
In the layer map, inline with answers what extra rules apply to one call. Use it for call-local intent, never for relaxation or reusable policy.
Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator prints a harmless WIDTHTRUNC warning for if (!obj.randomize()), because randomize() returns an int, and an UNSATCONSTR warning when a solve fails on purpose. Icarus 13 does not support constraints.