SystemVerilog · Module 10
Constraint Modes — constraint_mode()
Per-object, per-block enable bitmap; surgical block-level disable vs whole-object disable; save/restore discipline; inheritance-aware access; TLM-port leak gotchas.
Module 10 · Page 10.6
What constraint_mode() Does
Every named constraint block in a class has a runtime mode flag — either ON (1) or OFF (0). All constraints start ON by default. constraint_mode() lets you flip this flag for a specific constraint, or for all constraints on an object at once, without changing the class source code.
A constraint that is OFF is completely invisible to the solver — as if it were never written. The moment you turn it back ON, it participates in every subsequent randomize() call on that object.
class Packet;
rand bit [7:0] src;
rand bit [15:0] len;
constraint no_broadcast { src != 8'hFF; }
constraint valid_length { len inside {[64:1518]}; }
endclass
Packet p = new();
// ── Turn OFF one constraint ────────────────────────────────────
p.no_broadcast.constraint_mode(0); // src is now free (0–255)
assert(p.randomize()); // only valid_length active
// ── Turn it back ON ───────────────────────────────────────────
p.no_broadcast.constraint_mode(1); // src excluded from 0xFF again
assert(p.randomize());
// ── Turn OFF ALL constraints on the object ────────────────────
p.constraint_mode(0); // every block is OFF
assert(p.randomize()); // fully unconstrained
// ── Turn ALL constraints back ON ──────────────────────────────
p.constraint_mode(1); // all blocks ON againQuerying the Current Mode
Called with no argument, constraint_mode() returns the current mode as an int: 1 if ON, 0 if OFF. Use this to write conditional logic or to save and restore state around a test sequence.
Packet p = new();
// ── Query a single constraint's mode ──────────────────────────
int mode = p.no_broadcast.constraint_mode();
$display("no_broadcast is %s", mode ? "ON" : "OFF");
// ── Save, modify, restore pattern ────────────────────────────
int saved = p.valid_length.constraint_mode(); // save current state
p.valid_length.constraint_mode(0); // disable temporarily
assert(p.randomize() with { len > 1518; }); // test oversized packet
p.valid_length.constraint_mode(saved); // restore to original stateObject-Level constraint_mode()
Calling constraint_mode() on the object handle (not on a specific constraint) operates on every constraint block in the class and all parent classes in one call. It is the fastest way to strip all restrictions off an object for an unconstrained stress test or to restore them all afterwards.
class BusTxn;
rand bit [31:0] addr;
rand bit write;
rand bit [2:0] burst;
constraint aligned { addr[1:0] == 2'b00; }
constraint in_range { addr inside {[32'h4000_0000:32'h4FFF_FFFF]}; }
constraint max_burst { burst <= 3'h4; }
endclass
BusTxn t = new();
// Phase 1 — normal constrained traffic
repeat (50) begin assert(t.randomize()); end
// Phase 2 — turn off EVERYTHING for raw stress test
t.constraint_mode(0);
repeat (50) begin assert(t.randomize()); end // any addr, any burst
// Phase 3 — restore all, resume constrained traffic
t.constraint_mode(1);
repeat (50) begin assert(t.randomize()); endconstraint_mode() Across Inheritance
A derived class object carries constraint blocks from both the parent and the child. constraint_mode() at the object level affects all of them. Per-constraint calls can target both parent and child constraints by name — through the same handle.
class Base;
rand bit [7:0] x;
constraint base_limit { x < 128; } // parent constraint
endclass
class Derived extends Base;
rand bit [7:0] y;
constraint child_limit { y > 10; } // child constraint
endclass
Derived d = new();
// Disable only the parent's constraint through the derived handle
d.base_limit.constraint_mode(0); // x is now free (0–255)
assert(d.randomize()); // child_limit still active
// Object-level call disables both base_limit AND child_limit
d.constraint_mode(0);
assert(d.randomize()); // x and y fully unconstrained
// Restore all
d.constraint_mode(1);Combining OFF Constraints With the with Clause
Turning a class constraint OFF does not prevent you from adding an inline with constraint that covers the same field. This pattern is extremely useful: disable the class's version of a rule, then provide a tighter or looser version inline for one call.
Packet p = new();
// Normal class constraint: valid_length forces len in 64–1518
// Test goal: generate an oversized packet (len > 1518)
// Step 1 — lift the class restriction
p.valid_length.constraint_mode(0);
// Step 2 — provide the test-specific range inline
assert(p.randomize() with { len inside {[1519:9000]}; });
// Step 3 — restore for subsequent calls
p.valid_length.constraint_mode(1);
assert(p.randomize()); // back to 64–1518Practical Patterns
Pattern 1 — Multi-phase test with escalating freedom
BusTxn t = new();
// Phase 1 — fully constrained normal traffic
repeat (100) begin assert(t.randomize()); drive(t); end
// Phase 2 — relax alignment only (out-of-alignment accesses)
t.aligned.constraint_mode(0);
repeat (20) begin assert(t.randomize()); drive(t); end
t.aligned.constraint_mode(1);
// Phase 3 — relax range (out-of-region accesses, expect error responses)
t.in_range.constraint_mode(0);
repeat (20) begin assert(t.randomize()); drive(t); end
t.in_range.constraint_mode(1);Pattern 2 — One base class, multiple test personalities
A single class can serve normal tests, stress tests, and error-injection tests — all from the same object — by toggling constraint modes between scenarios. No subclasses required.
typedef enum { NORMAL, STRESS, ERROR_INJECT } test_mode_e;
task automatic configure(BusTxn t, test_mode_e mode);
case (mode)
NORMAL: begin
t.constraint_mode(1); // all ON
end
STRESS: begin
t.constraint_mode(0); // all OFF — fully random
end
ERROR_INJECT: begin
t.constraint_mode(1); // start from all ON
t.in_range.constraint_mode(0); // allow out-of-range addresses
t.aligned.constraint_mode(0); // allow misaligned accesses
end
endcase
endtaskconstraint_mode() vs the Alternatives
| Tool | When to use it | Persists after call? |
|---|---|---|
constraint_mode(0) | Disable a rule for multiple consecutive calls | Yes — stays OFF until you turn it ON |
with { } inline | Override or narrow for exactly one call | No — discarded after the call |
| Child class override (same name) | Permanently replace a rule for a whole test class | Permanent — part of the class definition |
rand_mode(0) | Fix a field to its current value across calls | Yes — field stays non-random until re-enabled |
| Soft constraints (page 10.7) | Hint a preference but allow the solver to override it | Always active — solver decides whether to apply |
Common Pitfalls
Forgetting to restore after a test phase
If you call constraint_mode(0) in one test and never restore, the next test using the same object runs without that constraint. Always restore, or create a fresh object per test phase.
Object-level ON overwrites per-constraint state
t.constraint_mode(1) turns every constraint ON, including ones you had individually disabled. There is no "restore to previous per-constraint state" — save individual modes before calling the object-level form.
Disabling a constraint that another depends on
If constraint B uses the same field as constraint A, and A is disabled, the solver may still satisfy B with values A would have excluded. Think through the interaction before disabling.
Confusing constraint_mode with rand_mode
rand_mode(0) freezes a field's value. constraint_mode(0) removes a rule but the field is still randomised freely. They are completely independent switches.
Quick Reference
// ── Per-constraint control ────────────────────────────────────
obj.c_name.constraint_mode(0); // OFF — invisible to solver
obj.c_name.constraint_mode(1); // ON — active again
int m = obj.c_name.constraint_mode(); // query: 0=OFF, 1=ON
// ── Object-level control ──────────────────────────────────────
obj.constraint_mode(0); // ALL constraints OFF (incl. inherited)
obj.constraint_mode(1); // ALL constraints ON
// ── Save / restore pattern ────────────────────────────────────
int saved = obj.c_name.constraint_mode();
obj.c_name.constraint_mode(0);
// ... do special randomize calls ...
obj.c_name.constraint_mode(saved);
// ── Disable + replace inline ──────────────────────────────────
obj.c_name.constraint_mode(0);
assert(obj.randomize() with { /* replacement rule */ });
obj.c_name.constraint_mode(1);Verification Usage — Where constraint_mode() Lives in Real Testbenches
Production environments use constraint_mode() in three predictable places. Outside those three, every other use is usually a refactor signal — most often "the inline with clause would be cleaner here."
Error-injection test relaxing one specific protocol rule
The canonical use: a test that deliberately drives stimulus that violates one specific protocol rule (e.g., unaligned addresses) while keeping every other rule intact. The relevant constraint block is disabled before randomize and re-enabled after.
<code>class error_align_test extends uvm_test;
task run_phase(uvm_phase phase);
apb_xact tx = apb_xact::type_id::create("tx");
int saved = tx.c_addr_align.constraint_mode();
tx.c_addr_align.constraint_mode(0); // off only for this scenario
if (!tx.randomize() with { addr[1:0] != 2'b00; })
`uvm_fatal("ERR", "couldn't generate unaligned addr")
drive(tx);
tx.c_addr_align.constraint_mode(saved); // restore — defensive
endtask
endclass</code>Sequence-level scenario shaping for performance modes
A test scenario might want to switch between performance modes (high-throughput, low-power, error-stress) where each mode has a different set of active constraints. A small helper method set_mode(int mode) on the sequence flips the right combinations of constraint blocks on each transaction it generates.
<code>function void apb_seq::set_mode(perf_mode_e mode);
case (mode)
HIGH_THROUGHPUT: begin
req.c_back2back_disable.constraint_mode(0); // allow back-to-back
req.c_burst_max.constraint_mode(1); // limit burst length
end
LOW_POWER: begin
req.c_idle_gap.constraint_mode(1); // require idle gap
req.c_burst_max.constraint_mode(0);
end
endcase
endfunction</code>Coverage-driven post-hoc constraint disabling
Late in a regression cycle, certain coverage bins remain unhit because the always-on constraints exclude their values. The test layer disables specific blocks temporarily to drive stimulus that closes those bins, with a one-line comment justifying the disable.
Simulation Behavior — How the Solver Honours constraint_mode()
Per-object bitmap
Each object carries one bit per named constraint block — initialised to 1 (enabled) at new(). constraint_mode(0/1) writes the bit; constraint_mode() with no argument reads it. The state is per-instance, not per-class — two objects of the same class can have entirely different blocks enabled.
Solver consultation at every randomize call
At each randomize() call, the solver iterates over every constraint block visible in the object's class hierarchy and, for each, checks the per-object enabled bit. Disabled blocks contribute nothing to the constraint set; enabled blocks contribute their expressions. The check is O(number of blocks), negligible against the cost of the actual solve.
Object-level vs constraint-level disable
obj.constraint_mode(0) (without a constraint name) disables every constraint block on the object. obj.block_name.constraint_mode(0) disables only the named block. The two address different needs: the object-level form is for "I want unrestricted randomisation for this transaction"; the block-level form is for "I want one specific relaxation."
Inheritance chain
Constraint blocks from the parent class are part of every child object — and each inherits its own per-object enabled bit. Disabling child.parent_c_block.constraint_mode(0) is legal; the parent's block is accessed by name on the child handle.
<code>tx.c_addr_align.constraint_mode(0);
// Only the alignment constraint is off.
// Address map, byte strobes, length —
// all still apply.
if (!tx.randomize()) `uvm_fatal("","");
tx.c_addr_align.constraint_mode(1);</code><code>tx.constraint_mode(0);
// Every constraint is off.
// Addresses can be anywhere — even
// outside the legal map.
// Byte strobes can be illegal too.
// DUT will NAK most generated stimulus.
if (!tx.randomize()) `uvm_fatal("","");</code>Waveform Analysis — Tracing Which Constraints Were Active at Solve Time
Disabled constraints are invisible from the outside — the resulting stimulus might violate a class constraint, but you can't tell whether (a) the constraint was disabled or (b) the constraint has a bug. The way to make the disable state visible is to log it explicitly at the call site.
The instrumentation pattern
<code>function void apb_xact::post_randomize();
`uvm_info("RAND",
$sformatf("paddr=0x%08h c_align=%s c_legal_map=%s",
paddr,
this.c_align.constraint_mode() ? "ON" : "OFF",
this.c_legal_map.constraint_mode() ? "ON" : "OFF"),
UVM_HIGH)
endfunction</code>ASCII view — disabled blocks visible in the log
<code> 100ns RAND paddr=0x4000_0010 c_align=ON c_legal_map=ON
200ns RAND paddr=0x4000_0011 c_align=OFF c_legal_map=ON ← deliberately unaligned
300ns RAND paddr=0x4000_0044 c_align=ON c_legal_map=ON
400ns RAND paddr=0x4000_0013 c_align=OFF c_legal_map=ON ← deliberately unaligned
↑
tells you the disable
was intentional, not a bug</code>Diagnosing "the DUT got an illegal transaction" failures
When a DUT failure log shows "received illegal transaction," the first question is: was the constraint that should have prevented this disabled? With constraint_mode logging in post_randomize, the answer is immediate. Without it, the diagnosis requires re-reading every constraint_mode call upstream — across sequences, tests, and config objects.
Industry Insights — Hard-Won Lessons About constraint_mode()
Debugging Academy — Five Real Bugs From constraint_mode() Misuse
"Constraint stays disabled after the scenario ends"
DEBUGA test disables c_align for an error-injection scenario, then resumes the main loop. The main loop's transactions also generate unaligned addresses, even though it never intended to.
<code>task error_scenario();
tx.c_align.constraint_mode(0); // disable
if (!tx.randomize() with { addr[1:0] != 2'b00; }) `uvm_fatal("","");
drive(tx);
// BUG: forgot to re-enable c_align
endtask
task main_loop();
error_scenario();
repeat (100) begin // expects aligned addresses
if (!tx.randomize()) `uvm_fatal("","");
drive(tx);
// Many of these are unaligned — DUT NAKs them
end
endtask</code>constraint_mode changes persist on the object until explicitly changed again. The error scenario leaks its disable into the main loop.
Use the save/disable/restore pattern: int saved = tx.c_align.constraint_mode(); tx.c_align.constraint_mode(0); ... ; tx.c_align.constraint_mode(saved);. The restore makes the function self-contained — the main loop sees no side effects from error_scenario.
"Disabling once on the class doesn't affect new instances"
DEBUGA test disables c_align on a transaction handle at setup; new transactions constructed later generate aligned addresses again.
<code>function void test::run_phase(uvm_phase phase);
apb_xact tx = apb_xact::type_id::create("tx");
tx.c_align.constraint_mode(0); // disable on this instance
repeat (10) begin
tx = apb_xact::type_id::create("tx"); // BUG: new instance every loop
if (!tx.randomize()) `uvm_fatal("","");
drive(tx);
// Every iteration sees aligned addresses again
end
endfunction</code>constraint_mode state is per-object. Constructing a new tx resets every block to enabled. The initial disable applied to a transaction that was discarded at the first loop iteration.
Either (a) construct once outside the loop and reuse: apb_xact tx = apb_xact::type_id::create("tx"); tx.c_align.constraint_mode(0); repeat (10) begin if (!tx.randomize()) ... end, or (b) re-apply the disable inside the loop after each new(). Option (a) is cleaner when the goal is consistent constraint state across iterations.
"obj.constraint_mode(0) disabled too much"
DEBUGA test wanted to relax just alignment but called obj.constraint_mode(0) (no block name); resulting stimulus violates the legal address map, byte-strobe legality, and burst-length bounds.
<code>tx.constraint_mode(0); // BUG: object-level disable, not per-block
if (!tx.randomize() with { addr[1:0] != 2'b00; }) `uvm_fatal("","");
// Generated addr is outside the legal map AND unaligned</code>The form obj.constraint_mode(0) with no block name disables every constraint block on the object. The author wanted block-level disable: obj.block_name.constraint_mode(0).
Always name the specific block: tx.c_align.constraint_mode(0);. The other blocks (legal map, strobes, burst length) remain in effect, and only alignment is relaxed. Reserve the object-level form for the rare case where you genuinely want unrestricted randomisation (debug scaffolding, perhaps — never in test code).
"Disabled constraint state leaks across UVM components"
DEBUGA sequence disables c_align on a transaction handle before sending it to the driver. The driver re-randomises the transaction (which the sequence didn't expect), and the re-randomisation honours the disable, producing unaligned drives.
<code>task my_seq::body();
`uvm_create(req)
req.c_align.constraint_mode(0);
start_item(req);
finish_item(req); // hand off to driver
// Sequence assumed driver only drives, not re-randomises
endtask
task my_drv::run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
if (!req.randomize()) `uvm_fatal("",""); // BUG: driver re-randomises
drive_bus(req);
seq_item_port.item_done();
end
endtask</code>The driver is re-randomising the transaction (which is unusual but not illegal), and the disable state set by the sequence travels with the handle. The driver's randomize call honours the disable.
Drivers should not re-randomise transactions handed to them by sequences — they should drive what they receive. Remove the driver's randomize call. If the driver legitimately needs to randomise additional fields (e.g., local delay), use the named-argument form on a separate rand field: req.randomize(local_delay). Either way, the disable's leak is a symptom of architectural confusion about responsibility.
"Inherited constraint disabled on parent but still firing on child"
DEBUGA child class inherits c_align from the parent. The test disables it via the parent class name; the child class's transactions still generate aligned addresses.
<code>class parent_xact;
rand bit [31:0] addr;
constraint c_align { addr[1:0] == 2'b00; }
endclass
class child_xact extends parent_xact;
// inherits c_align
endclass
child_xact c = new();
// BUG: trying to disable via parent's type doesn't work
parent_xact::c_align.constraint_mode(0); // illegal syntax
c.c_align.constraint_mode(0); // CORRECT: through the instance handle</code>constraint_mode is a per-instance method. You must call it on an instance handle, not on a class type or via scope resolution. The inherited block is accessed by its name through the child handle — c.c_align.constraint_mode(0) — and the disable applies to that specific instance.
Always call through the instance handle: c.c_align.constraint_mode(0);. The inheritance chain is transparent — the parent's block is accessible by its declared name on the child handle. No type-scoped disable mechanism exists; everything is per-object.
Interview Q&A — Twelve Questions You Will Be Asked
It enables or disables a named constraint block on a specific class instance. constraint_mode(0) disables; constraint_mode(1) enables; calling with no argument reads the current state. The change is per-instance and persists until explicitly changed again. The class definition is never modified — only the per-object enable bitmap.
Best Practices — Ten Rules for Disciplined constraint_mode() Usage
- Always save the current mode before disabling.
int saved = c.constraint_mode(); c.constraint_mode(0); ... ; c.constraint_mode(saved);— four lines, future-proof. - Prefer per-block disable over per-object disable. Surgical relaxation preserves every other constraint; whole-object disable opens the floodgates.
- Pair every disable with a comment explaining why. Future readers should understand the scenario from the source, not from a code-archaeology session.
- Remember the state is per-object. Constructing a new instance resets every block to enabled. Apply your disable after every
new()or reuse the same handle. - Log
constraint_modestate inpost_randomizeduring bring-up. The log line makes "which constraint was off?" diagnosable in seconds instead of hours. - Don't leak disable state across function boundaries. A function that disables should restore before returning, so callers see no side effects.
- Don't leak disable state across TLM ports. Either restore before sending or clone the transaction so downstream code gets a fresh-state copy.
- Choose
constraint_modeover soft constraints when the relaxation is scenario-driven. Soft is for graceful in-class conflict resolution;constraint_modeis for explicit test-layer control. - For complex scenarios, use a helper class. A "mode manager" with
set_scenario_X(tx)methods centralises policy and keeps test code readable. - Use the inheritance-aware syntax.
child_obj.parent_block_name.constraint_mode(0)works exactly as expected; there is no separate "disable parent's block" mechanism.