Verification

Constrained randomization in SystemVerilog

By CourseTron Team ·  7 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    🖨  Download / Print this guide (PDF)

    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.

    What Is Constrained Randomization?

    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.

    Why It Matters and How the Solver Works

    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).

    Key fact: A 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.

    rand vs randc, and Constraint Variants

    SystemVerilog provides two randomization qualifiers and several kinds of constraints:

    • rand — a standard random variable. Each randomize() call draws a fresh value; repeats are allowed, so uniformity is only statistical.
    • randc — random-cyclic. The solver iterates through every value in the variable's range in a random permutation before any value repeats. Useful for exhaustively cycling small enumerations such as opcodes or FSM states. Because it must track a permutation, randc is typically limited to narrow-width variables.
    rand versus randcrandEach randomize() call drawsa fresh value.Repeats are allowed, so uniformityis only statistical.randcIterates through every value in arandom permutation first.No repeat until the range is used;typically narrow-width variables.CourseTron · coursetron.com
    rand draws a fresh value every call, while randc cycles through the whole range in random order before any value repeats.

    Constraints themselves come in several forms:

    • Hard constraints — the default. They must be satisfied or randomize() fails.
    • Soft constraints — declared with the 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.
    • Inline constraints — added at the call site with obj.randomize() with { ... }, letting one test tighten the object's class constraints for that call only.
    • Implication and if-else — mode == BURST -> len inside {[4:16]}; makes one relation conditional on another.
    • Distribution (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...before — 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.
    From rand variables and constraints to legal stimulusrand / randcvariablesconstraintblocks + distConstraintsolverlegal randomstimulusCourseTron · coursetron.com
    Flow from rand and randc variables and constraint blocks through the constraint solver to legal random stimulus.

    How It Is Used and Debugged in Practice

    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.

    randomize(): one solve, return 1 or 0obj.randomize()calledConstraint solverall at oncereturns 1satisfiablereturns 0unsatisfiableCourseTron · coursetron.com
    randomize() hands all rand variables and enabled constraints to the solver together; it returns 1 on success or 0 if the constraints are unsatisfiable.
    Learn this hands-on

    Real projects and industry tool flows, with certification.

    Explore CourseTron Courses →

    How Industry Tools Handle It

    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.

    constrained randomization — Interview FAQ

    What is the difference between rand and randc?

    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.

    Does the order of statements inside a constraint block matter?

    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.

    What happens when randomize() cannot satisfy the constraints?

    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.

    What is a soft constraint and when would you use it?

    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.

    Advertisement