Constrained randomization
- Declare
randandrandcproperties - Write constraint blocks that keep stimulus legal
- Call
randomize()and always check its result - Add inline constraints with
randomize() with - Use
pre_randomizeandpost_randomize - Reproduce a failing test from its seed
Explanation
Why constrained random
Directed tests check the cases an engineer thought of. Constrained-random stimulus explores combinations nobody listed, while constraints keep every value legal for the protocol. The result is measured with functional coverage (lesson 11), not by counting tests.
rand and randc
class bus_txn;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit write;
randc bit [2:0] tag; // cycles through all 8 values before repeating
constraint c_addr { addr inside {[8'h00:8'h7F]}; }
constraint c_align { addr[1:0] == 2'b00; }
endclass
| Modifier | Distribution | Use |
|---|---|---|
rand |
Each call picks independently; repeats are possible | Most fields |
randc |
Random permutation: every value once per cycle | Small fields that must all be visited, such as IDs |
Only properties marked rand or randc are changed by randomize(). Others keep their current values but can still appear in constraints.
Constraint blocks
A constraint is a set of Boolean expressions that must all be true. The solver chooses values that satisfy every active constraint at once; constraints are declarative, not executed in order.
constraint c_len {
len inside {[1:16]};
write -> len <= 8; // writes are at most 8 beats
}
Useful operators: inside for sets and ranges, -> and if/else for conditions, and relational and arithmetic expressions. Keep arithmetic widths in mind: a + b == sum with 8-bit a, b and sum is evaluated at 8 bits, so the solver may pick a = 200, b = 100, sum = 44 because the addition wraps. Cast to a wider type, for example 9'(a) + b == sum, when wrap-around is not intended.
Calling randomize
randomize() returns 1 on success and 0 if the constraints cannot be satisfied. On failure the rand properties keep their previous values, so an unchecked failure silently repeats old stimulus.
bus_txn t = new();
if (!t.randomize())
$fatal(1, "bus_txn randomize failed");
if (!t.randomize() with { write == 1; addr == 8'h40; })
$fatal(1, "inline constraint conflict");
randomize() with { ... } adds constraints for one call only. Inside the with block, names resolve to the object being randomized first.
pre_randomize and post_randomize
These methods are called automatically before and after the solver runs. Use post_randomize() to compute derived, non-random fields such as a parity bit or CRC.
bit parity;
function void post_randomize();
parity = ^data;
endfunction
Seeds and reproducibility
Random stimulus is repeatable only if the seed is known. Record the seed in every log and allow it to be set from the command line, for example with a plusarg read by $value$plusargs("seed=%d", seed) and applied with process::self().srandom(seed). A failing regression test must be re-runnable with the same seed and the same source.
Tool note: Constrained randomization runs on Verilator 5.x only when a constraint solver (z3 or cvc5) is installed; without one, randomization fails at run time. Icarus 12 does not support rand or constraints.
Worked example
With c_addr and c_align, legal addresses are 0x00, 0x04 … 0x7C: 32 values. Calling t.randomize() with { addr == 8'h81; } conflicts with c_addr, so randomize() returns 0 and addr keeps its old value. If the test does not check the return value, it continues and drives the previous address again, and the intended out-of-range test never happens.
Check yourself
How do rand and randc differ? What happens to rand fields when randomize() fails? Why are constraints not executed in order? Why must the seed be logged?
Practice: Constrained randomization
Write a packet class with a rand length (1–64), a rand payload type (enum of SHORT, LONG, ERROR), a randc 3-bit ID and a non-random checksum computed in post_randomize(). Constrain SHORT packets to length 8 or less. Randomize 50 packets, check every return value, and print a histogram of lengths. Add an inline constraint that forces an ERROR packet and one that conflicts, and show how the failure is reported.
Practice work is non-certified and is not stored by ASIC Campus.