Difficulty: Hard · Puzzle 1 of 6 · Topic: randomize() with Puzzles
For the shown randomize() call, does randomization always pass, always fail, or depend on solver luck? Explain pre/post hook behavior too.
class txn;
rand bit is_write;
rand bit [7:0] addr;
rand bit [3:0] len;
bit lock_len;
constraint c_base {
addr inside {[0:255]};
is_write -> (len inside {[1:8]});
!is_write -> (len inside {[4:12]});
}
function void pre_randomize();
if (lock_len) len = 4;
endfunction
function void post_randomize();
if (is_write && (len > 8)) $error("illegal write len");
endfunction
endclass
txn t = new();
t.lock_len = 1;
assert(t.randomize() with {
is_write == 1;
(addr inside {[16:31]}) -> (len inside {[9:12]});
});Inline with-clause is conjoined with class constraints, not overriding them. Count the solutions instead of simulating the solver.
1) The solver does not pick addr and then backtrack. It works on the solution set: constraints are ANDed.
2) is_write==1 forces len in 1..8. The inline implication requires len in 9..12 whenever addr is in 16..31; that is impossible together with len in 1..8, so the antecedent must be false: addr must NOT be in 16..31.
3) Solutions: 240 allowed addr values x 8 len values (1..8) = 1920. The set is never empty, so the call ALWAYS passes (no solver luck). Executed 400 times on Verilator 5.048: 0 failures, addr never in 16..31, len took all of 1..8.
4) pre_randomize's `len = 4` does NOT pin len: len is rand, so the solver overwrites it (len took all of 1..8 although lock_len=1). Locking requires len.rand_mode(0).
5) post_randomize runs after the successful solve; its $error can never fire because is_write implies len<=8.Answer: Always passes: 1920 solutions (240 addr values outside 16..31 x len 1..8). pre_randomize's len=4 is overwritten by the solver, and post_randomize's $error can never fire.
Checked by running a model of the rule in a Python model and Verilator 5. Exact enumeration of the constraint set per the LRM (the LRM defines the distribution; simulator solvers need not be uniform). Counts the (addr,len) pairs satisfying the class constraints AND the inline constraints with is_write==1. Verilator 5.048: 400 calls with lock_len=1 never fail, never put addr in 16..31, keep len in 1..8, never reach the $error, and the solver overwrites pre_randomize's len=4.
It looks like a conflict (len in 1..8 vs 9..12) but an implication whose antecedent can be made false is not a conflict: addr in 16..31 is simply excluded from the solution set. Two further traps: assigning a rand variable in pre_randomize does not lock it, and 'depends on solver luck' is never the right answer for a satisfiable set.
How do you really lock len to 4 for this call? Model answer: set `t.len = 4; t.len.rand_mode(0);` before randomize. len is now a state variable equal to 4, which satisfies is_write -> len in 1..8, so the call still passes with 240 solutions (addr outside 16..31). If you froze len at 9 the call would fail, because is_write==1 requires len<=8.
The call always passes. When is_write is one, len must be one to eight, so the inline implication can only be satisfied if addr is outside sixteen to thirty-one; that is not a conflict, it just removes those addresses, leaving 1920 solutions. It is not solver luck, because the solution set is never empty. The traps are that assigning len in pre_randomize does not pin a rand variable, since the solver overwrites it, and that the post_randomize error can never fire. Next I would lock len with rand_mode zero if I really need it fixed.