Let's do a small, terrifying calculation. A 32-bit adder takes two 32-bit inputs, so its input
space has
Pure random stimulus explores without prejudice: it wanders into corners no test plan lists —
the carry that ripples all 32 bits, the back-to-back operations with a stale flag, the address
that aliases a cache line. Paired with the
But unconstrained randomness has a fatal flaw: most random bit patterns are illegal inputs. A random 32-bit word is almost never a valid one-hot select (32 valid patterns out of 4,294,967,296); a random address ignores alignment rules; a random opcode byte is mostly gibberish the decoder may rightly reject. Feed a DUT garbage and it fails for boring, spec-permitted reasons — noise that buries real bugs. The fix is constrained random: declare the rules of legality, and let a solver pick freely within them.
The rand variables are the dice; each constraint block is a loading rule;
and randomize() invokes a genuine constraint solver that finds a value
assignment satisfying every rule at once (it can handle constraints that interlock, like
addr ranges that depend on op). The dist weighting tilts the
dice — here 60% reads — because realistic traffic mixes find different bugs than uniform ones.
Constraints are your machine-readable spec of legal inputs: writing them forces the team to
say precisely what the design must tolerate.
The dice are not truly random — they are a pseudo-random sequence unrolled from a starting seed. Same seed, same sequence, bit for bit, forever. This turns randomness from a liability into an instrument: a nightly regression runs thousands of seeds; when seed 8,675,309 fails at 3 a.m., the log records it, and in the morning you rerun exactly that seed and watch the identical failure under a debugger. A failing seed is a treasure — it is a bug, gift-wrapped, reproducible on demand. Losing the seed (or letting the bench draw entropy the seed doesn't control) means a bug you saw once and may never see again; verification teams treat that as a process failure on par with losing the RTL itself.
Watch the whole machinery run — a seeded generator, constraints, weighted ops, and perfect reproducibility:
The two seed-42 lines are identical — that is reproducibility, and every address is aligned and in range — that is legality. Randomness with an audit trail.
inside
ranges, alignment, dist weights); a solver picks freely within them;
A subtle profession-within-the-profession: the constraint solver itself can bias your dice.
Solvers must pick uniformly-ish among the legal solutions, but a carelessly-written
constraint set can make some legal values astronomically rarer than others — for instance,
constraining a + b == c with all three random may make tiny values of c
vastly over-represented depending on solve order. SystemVerilog even lets you control it
(solve a before b). Verification teams therefore measure their randomness:
histogram the generated values, eyeball the distribution, and — in the next lesson's language —
collect coverage on the stimulus as well as the design. Trust, but verify, applies to your
own dice too. And when the histogram looks wrong, the fix is almost always to simplify the
constraints rather than to fight the solver.
Two ditches, one road. Drive with no constraints and you mostly generate illegal stimulus: random one-hot selects that aren't one-hot, misaligned addresses, opcodes from outer space. The DUT "fails" in ways the spec never promised to handle, your triage queue fills with non-bugs, and the team learns to ignore red — the worst possible culture. But the opposite ditch is quieter and deadlier: over-constraining. Every constraint you add is a region of input space you have promised never to explore. Constrain addresses to "nice" ranges and you will never test the boundary; force operations to alternate read/write and the back-to-back-write bug ships. The discipline: constraints must encode exactly the spec's definition of legal — no tighter, no looser. When the spec says "any address", your constraints must eventually try the ugly ones: zero, all-ones, the last word before wraparound. If a constraint exists only to make failures stop, you haven't fixed a bug — you've stopped looking for it.