Part 3 · Constraint Randomization · Intermediate

constraint_mode(): Toggling Constraint Blocks

Enabling and disabling named constraint blocks, structuring constraints for controllability, and error injection.

Named blocks are controllability handles

constraint_mode(0) deactivates a named constraint block so the solver ignores every expression inside it; constraint_mode(1) reactivates it. Like rand_mode, it has block-level and object-level forms plus a per-block query, and the setting is sticky instance state. The deeper point: constraint_mode is only as useful as your block organization . A class with one giant anonymous-purpose block gives tests nothing to grab; a class with constraints grouped by intent — validity, performance shaping, addressing policy — lets tests switch off exactly one concern.

systemverilog
class eth_pkt;
  rand bit [13:0] len;
  rand bit [47:0] dst;
  rand bit        bad_fcs;

  // Grouped BY INTENT — each block is a test-facing knob:
  constraint valid_c   { len inside {[64:1500]}; bad_fcs == 0; }
  constraint short_c   { soft len < 256; }            // perf shaping
  constraint unicast_c { dst[40] == 0; }               // addressing policy
endclass

module t;
  initial begin
    eth_pkt p = new();
    p.short_c.constraint_mode(0);          // allow full-size frames
    void'(p.randomize());

    if (p.valid_c.constraint_mode())       // query: is validity active?
      $display("still generating legal frames");
  end
endmodule

Convention matters: most teams suffix blocks with _c and name them for the rule they enforce (valid_c, align_c, no_err_c), precisely so test writers can disable them without reading the expressions.


Error injection by disabling valid_c

The canonical use case. The base transaction constrains itself to protocol-legal values in a clearly named block. An error test disables that one block — and usually adds an inline constraint to steer which illegality appears, since “anything goes” randomization mostly produces boring garbage rather than the interesting near-legal corners.

systemverilog
class err_test_seq;
  task body(eth_pkt p);
    // 1. drop legality
    p.valid_c.constraint_mode(0);

    // 2. steer toward an interesting illegal corner: runt frames
    if (!p.randomize() with { len inside {[1:63]}; bad_fcs == 1; })
      $fatal(1, "error-injection randomize failed");

    // 3. ALWAYS restore — the next sequence expects legal frames
    p.valid_c.constraint_mode(1);
  endtask
endclass

Note the inline with succeeds only because valid_c is off — with it on, len inside {[1:63]} contradicts len inside {[64:1500]} and randomize() returns 0. constraint_mode is the relax mechanism; inline with is the tighten mechanism; error injection typically needs both, in that order.


Block state model

diagram
CONSTRAINT BLOCK STATES (per object instance)

                 constraint_mode(0)
        ┌────────────────────────────────┐
        ▼                                │
   ┌─────────┐                      ┌─────────┐
   │ DISABLED │                      │ ENABLED │ ◄── default at new()
   │ block    │                      │ block   │
   │ ignored  │                      │ solved  │
   └─────────┘                      └─────────┘
        │                                ▲
        └────────────────────────────────┘
                 constraint_mode(1)

   object-level:  p.constraint_mode(0)   ALL blocks DISABLED
                  p.constraint_mode(1)   ALL blocks ENABLED
   query:         p.valid_c.constraint_mode()  0 or 1  (per block)

   sticky: state persists across randomize() calls and across
   sequences sharing the object — restore what you disable.

Two scoping facts worth knowing: the mode belongs to the object instance, not the class (two objects of the same class can differ), and a subclass that overrides a block by name owns a single mode for that name — there is no separate toggle for the hidden parent version.


constraint_mode vs the alternatives

  • vs soft constraints — soft yields automatically on contradiction; constraint_mode is an explicit procedural switch. Defaults want soft; legality rules that error tests remove want a named hard block.

  • vs subclass override — override replaces a block's CONTENT permanently for that type; constraint_mode toggles existence per instance at runtime.

  • vs inline with — with can only add; it can never turn off a class constraint. Any “relax” requirement is constraint_mode or inheritance territory.

  • vs rand_mode — rand_mode freezes variables; constraint_mode silences rules. Freezing a field leaves its constraints checking the frozen value; disabling a block leaves its fields fully random.


Interview angle

What interviewers ask

  • “How do you inject protocol errors with a constrained-random transaction?” — name the legality block (valid_c), constraint_mode(0) it, add an inline with steering the illegality, restore afterward.

  • “Why organize constraints into multiple named blocks?” — controllability: blocks are the unit constraint_mode toggles and the unit subclasses override.

  • “constraint_mode vs rand_mode?” — constraints vs variables; disabling a block frees its fields, freezing a field keeps its constraints as checks.

  • “Is the mode shared across objects of the class?” — no; it is per-instance sticky state.

  • “Can inline with re-enable behavior a disabled block provided?” — with only adds expressions; it neither disables nor enables blocks.

Key takeaways

  • constraint_mode(0/1) toggles named blocks per object instance; default is enabled.

  • Organize constraints by intent into named blocks — that naming IS the test-control API.

  • Error injection = disable valid_c, then tighten with inline constraints toward interesting illegality.

  • Mode is sticky per-instance state — restore blocks you disable.

  • constraint_mode relaxes; inline with tightens; they compose and are not interchangeable.

Common pitfalls

  • One monolithic constraint block — tests must choose all-or-nothing, killing reuse.

  • Disabling valid_c and forgetting to re-enable it — later sequences silently generate illegal traffic.

  • Expecting object-level constraint_mode(1) to restore a custom mix — it enables ALL blocks, clobbering intent.

  • Trying to relax a constraint via inline with — impossible; with intersects, it never removes.

  • Assuming a disabled block in the parent stays disabled in a subclass override scenario — the override owns one mode for that block name.