Part 3 · Constraint Randomization · Intermediate

Q&A: Controlling Randomization

rand_mode vs constraint_mode, inline constraints and whether they can relax class constraints, subclass overrides, per-test disabling, freezing a field.

Q: What is the difference between rand_mode and constraint_mode?

Direct answer: they switch off different things. rand_mode(0) on a variable stops the solver from assigning it — it becomes a state variable holding its current value, which constraints still read as a constant. constraint_mode(0) on a named constraint block deactivates that block — its variables stay rand but lose those restrictions. Variable knob vs constraint knob; both per-instance, both also callable with no argument to query the current state.

systemverilog
class txn;
  rand bit [7:0] addr, len;
  constraint c_addr { addr inside {[10:20]}; }
  constraint c_len  { len < addr; }
endclass

initial begin
  txn t = new();

  t.addr = 15;
  t.addr.rand_mode(0);        // addr frozen at 15; c_len now reads addr=15
  void'(t.randomize());       // only len solved, len < 15

  t.c_addr.constraint_mode(0); // addr rand again below, but unrestricted
  t.addr.rand_mode(1);
  void'(t.randomize());        // addr any 8-bit value; len < addr still holds
end

Follow-up you should expect

“What happens to a constraint that mentions a rand_mode(0) variable?” — It stays active with the variable read as a constant. That can silently shrink another variable’s space — freeze addr at 0 above and len < addr becomes len < 0: infeasible, randomize() returns 0. Interviewers love this interaction because juniors assume freezing one variable is always safe.

Junior vs senior answer

  • Junior: “rand_mode disables a variable, constraint_mode disables a constraint.”

  • Senior: adds that a frozen variable is still read by active constraints as a constant — and demonstrates the induced-infeasibility interaction unprompted.


Q: Can an inline with-constraint RELAX a class constraint?

Direct answer: no — and this is one of the most-failed questions. Inline with constraints are ANDed with all active class constraints; the solver must satisfy both sets simultaneously. You can only narrow, never widen. If the inline clause contradicts a hard class constraint, randomize() returns 0 — it does not “prefer” the inline one. The only class constraints an inline clause can override are soft ones, because soft yields to any conflicting hard constraint by definition.

systemverilog
class txn;
  rand bit [7:0] len;
  constraint c_len { len inside {[1:16]}; }        // hard
endclass

initial begin
  txn t = new();
  // ANDed: {1..16} ∩ {32..64} = ∅ → randomize() returns 0
  if (!t.randomize() with { len inside {[32:64]}; })
    $display("FAILS — inline cannot relax a hard class constraint");

  // To truly relax: disable the named block, then constrain inline
  t.c_len.constraint_mode(0);
  void'(t.randomize() with { len inside {[32:64]}; });   // now legal
end

Follow-up you should expect

“So how should the class have been written if tests need lengths beyond 16?” — Either soft len inside {[1:16]} as a default that melts on contact, or keep the hard constraint only for genuinely illegal values and let policy live in tests. This loops back to legality-vs-policy: hard class constraints should encode what the protocol forbids, not what tests usually want.

Junior vs senior answer

  • Junior: “yes, inline constraints override the class.” — the precise misconception this question hunts.

  • Senior: “no — conjunction, not override; contradiction fails the solve; soft is the mechanism for overridable defaults; constraint_mode is the escape hatch for hard ones.”


Q: How do you override a constraint in a subclass?

Direct answer: define a constraint with the same name in the subclass — it replaces the base version entirely (constraints override like virtual methods). A constraint with a new name is simply added and ANDs with everything inherited. Same-name-replaces / new-name-ANDs is the rule interviewers check, because mixing them up produces either accidental contradictions or accidentally vanished restrictions.

systemverilog
class base_txn;
  rand bit [7:0] len;
  constraint c_len { len inside {[1:16]}; }
endclass

class jumbo_txn extends base_txn;
  // SAME name → replaces base c_len completely
  constraint c_len { len inside {[64:255]}; }
endclass

class narrow_txn extends base_txn;
  // NEW name → ANDs with inherited c_len → effective range [8:16]
  constraint c_min { len >= 8; }
endclass

Follow-up you should expect

“What if the subclass wants the base constraint mostly intact, plus one tweak?” — That is the new-name-AND case (narrow_txn above). And “what if I want to remove a base constraint without replacing it?” — same-name with an empty body constraint c_len { } effectively deletes it, or call constraint_mode(0) on it at runtime. Having all three moves (replace, extend, delete) ready is the senior signature.

Junior vs senior answer

  • Junior: “just write the constraint again in the subclass.” — doesn’t know whether that replaces or accumulates.

  • Senior: states same-name-replaces vs new-name-ANDs explicitly, and lists replace / extend / empty-body-delete as distinct tools.


Q: A test needs ONE constraint disabled for ONE test only — how?

Direct answer: call obj.<name>.constraint_mode(0) on the instance before randomizing — which is exactly why production transaction classes give every constraint a meaningful name: an anonymous constraint cannot be targeted. The disable is per-instance and persists for that handle until re-enabled; in a UVM flow you would do it in the test or via a factory-overridden subclass so the env code stays untouched.

systemverilog
class err_test;
  task body(txn t);
    // This test explores illegal alignment deliberately:
    t.c_align.constraint_mode(0);
    if (!t.randomize() with { addr[1:0] != 2'b00; })
      $fatal(1, "randomize failed");
    // ... drive, check DUT rejects it ...
    t.c_align.constraint_mode(1);   // restore for any reuse of this handle
  endtask
endclass

Follow-up you should expect

“Why not just extend the class and empty out the constraint?” — Also valid, and better when the relaxation defines a whole test family (use the factory to substitute the subclass). constraint_mode is better for a one-off inside a single test. The trade: constraint_mode is runtime state that someone can forget to restore; a subclass is declarative and visible in the test’s type. Articulating that trade is the answer’s senior half.

Junior vs senior answer

  • Junior: “use constraint_mode(0).” — names the knob.

  • Senior: names the knob, the naming-convention prerequisite, the restore discipline, and when a factory-substituted subclass is the better structure.


Q: How do you freeze a field at a specific value during randomize?

Direct answer: three idioms, in order of preference. (1) Inline equality — randomize() with { addr == 32'h1000; } — keeps the field rand, composes with all constraints, scope limited to the call. (2) rand_mode(0) after setting the value — persistent freeze across many calls, but remember active constraints still read it. (3) A configuration knob: a state variable plus a guarded constraint like use_fixed -> addr == fixed_addr — the reusable-environment version where tests set knobs instead of touching solver controls.

systemverilog
// Idiom 1 — one call:
void'(t.randomize() with { addr == 32'h1000; });

// Idiom 2 — many calls:
t.addr = 32'h1000;
t.addr.rand_mode(0);
repeat (20) void'(t.randomize());

// Idiom 3 — knob in the class:
class txn;
  rand bit [31:0] addr;
  bit             use_fixed;
  bit [31:0]      fixed_addr;
  constraint c_fix { use_fixed -> addr == fixed_addr; }
endclass

Follow-up you should expect

“What if the frozen value violates a class constraint?” — Idiom 1 and 3: the conjunction is infeasible and randomize() returns 0 — loud, good. Idiom 2: the constraint reads the frozen constant, and any OTHER variable constrained against it may become infeasible — the same induced-failure interaction from the rand_mode question. Loud failure beats silent skew; prefer idiom 1 unless persistence is required.

Junior vs senior answer

  • Junior: knows one idiom, usually rand_mode(0).

  • Senior: ranks all three by scope and composability, and explains the failure mode of each when the frozen value conflicts with remaining constraints.

Key takeaways

  • rand_mode gates variables; constraint_mode gates named blocks — and frozen variables are still read.

  • Inline with-constraints AND with class constraints — they narrow, never relax; only soft yields.

  • Subclass rule: same name replaces, new name ANDs, empty body deletes.

  • Freeze idioms: inline equality (one call), rand_mode (persistent), knob constraint (reusable env).

Common pitfalls

  • Anonymous constraints in transaction classes — nothing to target with constraint_mode.

  • Assuming inline with overrides hard class constraints — it conjoins; contradiction fails the solve.

  • Forgetting to restore constraint_mode/rand_mode on shared handles — state leaks into later randomizations.

  • Freezing a variable that other constraints depend on without re-checking feasibility.