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.
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
endFollow-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.
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
endFollow-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.
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; }
endclassFollow-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.
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
endclassFollow-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.
// 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; }
endclassFollow-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.