The short answer: Constrained randomisation lets a testbench generate legal but varied stimulus. Declare variables rand (any value, repeats allowed) or randc (cycles through all values before repeating), describe legal values with constraint blocks (inside, dist, implication, foreach, unique, soft, solve…before), and call randomize(), adding inline constraints with randomize() with {…} for one test. Always check the return value: 0 means the constraints conflict and nothing was changed.
Random stimulus finds the bugs nobody thought to test, but only if the constraints are right. Too loose, and you waste time on illegal traffic; too tight, and you never reach the corner cases. This post covers the constraint features I use most and the mistakes I see, and combines my two older posts on randomisation.
- How randomize() works
- A realistic example
- Constraint features
- Controlling randomisation from a test
- rand vs randc
- Mistakes I see often
Table of Contents
Part 1: How randomize() works

Part 2: A realistic example
typedef enum bit [1:0] {READ, WRITE, IDLE} kind_t;
class bus_txn;
rand kind_t kind;
rand bit [31:0] addr;
rand bit [7:0] len;
rand bit [31:0] data[];
constraint c_addr { addr inside {[32'h0000_0000:32'h0000_FFFF]};
addr[1:0] == 2'b00; } // word aligned
constraint c_len { len inside {[1:16]}; }
constraint c_data { data.size() == len; }
constraint c_kind { kind dist { READ := 45, WRITE := 45, IDLE := 10 }; }
constraint c_idle { kind == IDLE -> len == 1; } // implication
constraint c_4k { (addr % 4096) + len*4 <= 4096; } // don't cross 4 KB
endclass
bus_txn t = new();
repeat (1000) begin
if (!t.randomize()) $fatal(1, "randomize failed");
drive(t);
end
Part 3: Constraint features
| Feature | Example | Use |
|---|---|---|
| inside | x inside {1, 2, [8:15]} | Sets and ranges |
| dist | x dist {0 := 1, [1:9] :/ 4} | Weighted values; := per value, :/ split across the range |
| Implication | mode == 1 -> x < 8 | Conditional constraints |
| if-else | if (big) len > 100; else len < 10; | Same, more readable for several branches |
| foreach | foreach (a[i]) a[i] < 100; | Constrain every element |
| unique | unique {a, b, c}; | All different |
| Array methods | a.sum() with (int'(item)) == 100 | Constrain sums, counts |
| soft | soft len == 4; | A default that inline constraints can override |
| solve before | solve kind before len; | Change the distribution, not the solution space |
Part 4: Controlling randomisation from a test
t.randomize() with { kind == WRITE; len == 4; }; // inline constraint
t.c_4k.constraint_mode(0); // turn a constraint off
t.addr.rand_mode(0); // freeze one variable
int x;
void'(std::randomize(x) with { x inside {[0:9]}; }); // outside a class
y = $urandom_range(10, 1); // quick random number
Part 5: rand vs randc
rand picks independently each time. randc cycles through every possible value in a random order before repeating, which is useful for small fields like an opcode where you want to hit every value quickly. Avoid randc on wide variables; the solver has to track the whole cycle.
Part 6: Mistakes I see often
- Ignoring the return value. A failed randomize() leaves old values, and the test happily sends the same transaction again.
- Overflow in sum constraints.
a.sum() == 100on 8-bit elements is computed in 8 bits; cast withwith (int'(item)). - Accidental bias. An implication like
big -> len > 200makes “big” rare if most lengths are small; use solve-before or dist. - Constraints that fight the test. Use soft constraints for defaults so tests can override them.
- No seed control. Make sure every run prints its seed, so failures can be reproduced.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my SystemVerilog series: Classes and OOP. Next: Threads and inter-process communication.
FAQ
What is the difference between rand and randc?
rand can repeat values; randc cycles through all possible values in a random order before repeating any.
What does solve before do?
It changes the probability distribution by choosing one variable first. It doesn’t change which solutions are legal.
What happens if constraints conflict?
randomize() returns 0 and leaves the variables unchanged. Always check the return value.
What is a soft constraint?
A constraint that gives a default but can be overridden by a conflicting constraint, usually an inline one, without causing a failure.
