field.rand_mode(0) removes a field from the variables the solver assigns. The field becomes state: its current value is read at solve time and every constraint on it must still hold.
len is frozen with rand_mode(0) while it still holds its starting value 0, and the class says len > 10. What does randomize() return?0. A frozen field is read as a constant, so len > 10 becomes 0 > 10, which no choice of the other fields can fix.
class pkt;
rand bit [7:0] len;
rand bit [7:0] payload_kind;
constraint c { len > 10; payload_kind < len; }
endclass
module tb_frozen_state;
initial begin
automatic pkt p = new();
p.len.rand_mode(0); // freeze len at its current value (0)
if (!p.randomize())
$display("frozen len holds 0, which breaks len > 10");
else $fatal(1, "FAIL: frozen len 0 should break len > 10");
p.len = 50; // set a legal value, then solve
if (!p.randomize()) // payload_kind solved in [0:49]
$fatal(1, "FAIL: randomize failed with len frozen at 50");
if (p.len != 50 || p.payload_kind >= 50)
$fatal(1, "FAIL: len=%0d payload_kind=%0d", p.len, p.payload_kind);
$display("PASS: len stayed 50 and payload_kind=%0d", p.payload_kind);
$finish;
end
endmodule%Warning-UNSATCONSTR: /var/folders/41/dgs80y1569b4x8pwflv_w9jr0000gn/T/rtbtb-K1WcxI/t16.sv:4: Unsatisfied constraint: 'constraint c { len > 10; payload_kind < len; }'
frozen len holds 0, which breaks len > 10
PASS: len stayed 50 and payload_kind=29Freezing without assigning first usually fails, since fields construct to zero. Setting a legal value before freezing is part of the idiom.
The call shape decides the scope. The query shape reports one variable's mode.
Call | Scope | Meaning |
|---|---|---|
| One field | len is read as state until re-enabled. |
| One field | len rejoins the solve. |
| Whole object | Every rand field in p freezes. |
| Whole object | Every rand field in p rejoins the solve. |
| One field | Query: returns 1 while randomized, 0 while frozen. |
An object-level query has no single mode to return, so only the per-variable query is legal. Mode is sticky instance state that outlives the call.
Production stimulus pins one field to a directed value while the rest stays random. The pin must satisfy the active constraints, and the mode must be restored after the loop.
class axi_txn;
rand bit [31:0] addr;
rand bit [7:0] len;
rand bit write;
constraint legal_c { len inside {[1:16]}; addr[1:0] == 0; }
endclass
module tb_hammer;
axi_txn t;
task hammer_one_address(bit [31:0] target, int n);
t.addr = target; // 1. set the directed value
t.addr.rand_mode(0); // 2. freeze it (it must satisfy addr[1:0]==0)
repeat (n)
if (!t.randomize()) // 3. len/write re-randomized each iteration
$fatal(1, "frozen addr breaks legal_c");
else if (t.addr != target)
$fatal(1, "FAIL: frozen addr moved to %h", t.addr);
t.addr.rand_mode(1); // 4. restore for the next user
endtask
initial begin
t = new();
hammer_one_address(32'hA000_0000, 100);
$display("PASS: 100 solves kept addr at A000_0000");
$finish;
end
endmodulePASS: 100 solves kept addr at A000_0000Field | rand_mode | At solve time |
|---|---|---|
addr | 0 | Read as the constant 32'hA000_0000; |
len | 1 | Solved: |
write | 1 | Solved |
Yes. x becomes a state variable read at solve time, so each constraint on x turns into a check against the frozen value. A frozen value outside the allowed range fails randomize().
When the pin must hold across many randomize() calls. rand_mode(0) keeps the pin without repeating the constraint, at the cost of sticky state that must be restored.
In the layer map, rand_mode answers which variables the solver assigns. Use it to pin knowns across calls, never to remove rules.
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.