In short: Constrained randomization is the SystemVerilog mechanism that lets a verification environment generate legal, high-coverage stimulus automatically by declaring rand/randc variables and the rules that bound them. Instead of hand-writing every test vector, you describe the space of valid values and let the solver draw from it.
Constrained randomization is a core feature of SystemVerilog used in constrained-random verification (CRV). You mark class properties as rand or randc, attach one or more constraint blocks that express the legal relationships between those properties, and then call the built-in randomize() method. The simulator's constraint solver computes a random assignment that satisfies every active constraint simultaneously, and returns 1 on success or 0 if the problem is unsatisfiable.
The purpose is to replace brittle, directed test vectors with stimulus that explores a far larger legal state space. A single randomized transaction class, called thousands of times with different seeds, can reach corner cases a human would never enumerate by hand, while still never producing an illegal packet, address, or opcode.
Modern designs have enormous input spaces. A protocol transaction might have an address, a burst length, a byte-enable mask, and a transfer type, with intricate legal combinations between them. Directed tests cover only what the engineer imagines; randomization, driven by a coverage model, systematically fills the gaps.
When randomize() is invoked, the solver gathers every rand/randc variable and every enabled constraint into one problem and solves them together — constraints are bidirectional relations, not sequential assignments. This is why ordering statements in a constraint block does not imply execution order; all expressions hold at once. If any combination of values can satisfy the whole set, the solver picks one, ideally with a uniform-ish distribution across the legal solution space (the LRM does not mandate perfect uniformity).
constraint block is declarative, not procedural. The solver treats all relations simultaneously, so a < b; b < c; constrains the whole triple at once — the line order carries no execution meaning.SystemVerilog provides two randomization qualifiers and several kinds of constraints:
randomize() call draws a fresh value; repeats are allowed, so uniformity is only statistical.randc is typically limited to narrow-width variables.Constraints themselves come in several forms:
randomize() fails.soft keyword. They express a preference (often a default value) that the solver honors only if nothing harder contradicts it; a later inline or stronger constraint can override a soft one without causing a failure.obj.randomize() with { ... }, letting one test tighten the object's class constraints for that call only.mode == BURST -> len inside {[4:16]}; makes one relation conditional on another.dist) — weights the probability of value ranges, e.g. len dist { 1 := 10, [2:8] :/ 40 }; where := assigns the weight to each value and :/ spreads the weight across the range.solve a before b; orders the solving of two variables to shape the resulting distribution. It does not change which solutions are legal, only their relative likelihood.In a UVM-style environment, randomization lives in the sequence item and sequence. A transaction class exposes its fields as rand; the base constraints encode protocol legality; and each test refines behavior with inline constraints or by disabling a named constraint via constraint_mode(0). Variables can also be excluded for a single call with rand_mode(0).
Two hooks let you extend solving without rewriting constraints. pre_randomize() runs before the solver and is handy for setting non-random state; post_randomize() runs after and is where you compute derived fields such as CRCs or fix up values the solver should not own.
The most common practical failure is a randomization failure — randomize() returns 0 because constraints conflict and no legal solution exists. Good practice is to always check the return (assert(txn.randomize())) so the failure is loud, then isolate the over-constrained variable, often by selectively disabling constraints or inspecting the solver's conflict report. Soft constraints exist precisely to avoid these dead ends when a preference is optional rather than mandatory.
Real projects and industry tool flows, with certification.
Explore CourseTron Courses →All three major simulators — Synopsys VCS, Cadence Xcelium, and Siemens Questa — ship IEEE 1800 constraint solvers, but each uses its own internal engine and heuristics, so the specific random values for a given seed differ between vendors even though every value is legal. This is expected: the LRM guarantees legality and reproducibility per tool and seed, not cross-vendor identical streams. Engineers rely on seed control for reproducible regressions, solver performance knobs for large constraint sets, and vendor conflict analyzers to debug failures. Coverage tools then close the loop by reporting which parts of the functional coverage model the randomized stimulus has actually hit.
A rand variable is drawn independently each call and values may repeat immediately. A randc variable is random-cyclic: the solver visits every value in its range in a random order before any value recurs, which guarantees exhaustive coverage of a small range but restricts it to narrow-width variables.
No. Constraint blocks are declarative; all relations are solved simultaneously. Statement order has no execution meaning. If you need to bias the solving order of variables to shape a distribution, use solve a before b; — but note that even this changes only probabilities, never the set of legal solutions.
randomize() returns 0 and leaves the random variables unchanged. You should assert on the return value so an over-constrained object fails visibly. Converting a mandatory preference into a soft constraint, or using constraint_mode(0) to disable a conflicting block, are the usual fixes.
A soft constraint states a default preference that the solver honors only if no hard constraint contradicts it. It is ideal for library defaults that individual tests may override inline without causing randomization failures.
Related: functional coverage in SystemVerilog · the VLSI Glossary · all courses.