Part 3 · Constraint Randomization · Intermediate
Dynamic Constraint Control
Hub — rand_mode, constraint_mode, inline with-constraints, inheritance override, state variables, and choosing a strategy.
Overview
A reusable transaction class ships with one set of fields and constraints, but every test wants different behavior: freeze this field, drop that legality rule, pin a value at one call site, or reshape everything for an error campaign. SystemVerilog gives you a layered toolkit for this: rand_mode() freezes individual fields, constraint_mode() toggles whole named constraint blocks, randomize() with {...} adds constraints at the call site, subclass constraint override replaces blocks by name, and non-rand state variables gate ranges from configuration. Senior-level verification is largely about choosing the right layer.
The interview thread running through this topic: each mechanism changes a different thing. rand_mode changes which variables are solved , constraint_mode changes which constraints are active , inline with adds (never removes) constraints, and inheritance replaces constraint blocks by name. Mixing up “adds” with “replaces” is the most common failure mode.
Sub-topics
rand_mode(): Freezing Fields — frozen fields become state but still participate in constraints.
constraint_mode(): Toggling Constraint Blocks — named blocks as controllability handles.
Inline Constraints: randomize() with — call-site constraints that AND with class constraints.
Constraint Override via Inheritance — same-named blocks replace; test-specific subclasses.
State Variables & Cross-Object Constraints — config knobs, obj.field references, nested rand objects.
Choosing a Control Strategy — decision flow across all five mechanisms.
DYNAMIC CONSTRAINT CONTROL — THE LAYERS
what you change mechanism scope
───────────────────────── ─────────────────────── ──────────────
which VARIABLES are rand_mode(0/1) per field /
solved vs read as state per object
which CONSTRAINTS are constraint_mode(0/1) per named block
active per object
ADD constraints at one randomize() with {...} per call site
call (intersect only!)
REPLACE a constraint subclass override per class/test
block by name (same block name) (+ factory)
PARAMETERIZE ranges non-rand state vars per config
from configuration read at solve time
Rule of thumb: tighten at the call site (with),
restructure per test (subclass/factory),
carve off legality for error tests (constraint_mode),
freeze knowns (rand_mode), parameterize with state.Key takeaways
Five layers: rand_mode (variables), constraint_mode (blocks), inline with (call-site add), inheritance (replace), state vars (parameterize).
Inline with can only tighten; subclass override and constraint_mode can relax.
Frozen rand_mode(0) fields still participate in constraints as state values.
Choosing the right layer is the actual interview question behind all of these.