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

  1. rand_mode(): Freezing Fields — frozen fields become state but still participate in constraints.

  2. constraint_mode(): Toggling Constraint Blocks — named blocks as controllability handles.

  3. Inline Constraints: randomize() with — call-site constraints that AND with class constraints.

  4. Constraint Override via Inheritance — same-named blocks replace; test-specific subclasses.

  5. State Variables & Cross-Object Constraints — config knobs, obj.field references, nested rand objects.

  6. Choosing a Control Strategy — decision flow across all five mechanisms.

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