Constrained randomization

Learning objectives
  • Declare rand and randc properties
  • Write constraint blocks that keep stimulus legal
  • Call randomize() and always check its result
  • Add inline constraints with randomize() with
  • Use pre_randomize and post_randomize
  • Reproduce a failing test from its seed
1 Objective→ 2 Learn→ 3 Example→ 4 Practice→ 5 Verify→ 6 Continue
How to study this lessonUnderstand the idea first. Use the example to see the behaviour, practise where provided, then make sure you can explain the result before continuing.

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?

Engineering practice

Practice: Constrained randomization

Task→Run / reason→Check result→Explain why

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.