Part 3 · Constraint Randomization · Intermediate

rand_mode(): Freezing Fields

Per-field and per-object rand_mode, frozen fields as state in constraints, set-then-freeze pattern, and querying.

What rand_mode(0) actually does

field.rand_mode(0) removes a field from the set of variables the solver assigns. Crucially, the field does not vanish from the constraint system — it becomes a state variable : its current value is read at solve time and every constraint mentioning it still must hold. This is the half people miss. Freezing a field does not disable the constraints on it; it converts those constraints into conditions on the frozen value, and if the frozen value contradicts them, randomize() fails.

systemverilog
class pkt;
  rand bit [7:0] len;
  rand bit [7:0] payload_kind;

  constraint c { len > 10; payload_kind < len; }
endclass

module t;
  initial begin
    pkt p = new();

    p.len.rand_mode(0);     // freeze len at its CURRENT value (0!)
    if (!p.randomize())
      $display("FAILS: frozen len==0 violates len > 10");

    p.len = 50;             // set a legal value, then solve
    void'(p.randomize());   // OK: payload_kind solved in [0:49]
  end
endmodule

Two lessons in one example: a frozen field keeps participating (payload_kind's range follows the frozen len), and freezing without first assigning a legal value is a randomize() failure waiting to happen — fields construct to zero.


Per-field vs per-object, and querying

Called on a single field, p.len.rand_mode(0) affects just that field. Called on the object, p.rand_mode(0) disables randomization of every rand/randc field in it (including, recursively, the mode of nested rand object handles being randomized — though each nested object also has its own rand_mode). Called with no argument as a function, it queries the current mode of a single variable and returns 0 or 1.

systemverilog
pkt p = new();

p.rand_mode(0);             // freeze EVERY rand field in p
p.len.rand_mode(1);         // re-enable just len

if (p.len.rand_mode())      // query — function form, single variable only
  $display("len is randomized");
if (!p.payload_kind.rand_mode())
  $display("payload_kind is frozen");

// NOTE: query form is per-variable; p.rand_mode() as a query is illegal —
// objects do not have a single mode to return.

Mode state lives on the object instance and is sticky: it persists across randomize() calls until changed. A sequence that freezes a field for one call must remember to thaw it, or every later randomize() of that object inherits the freeze.


The set-then-freeze pattern

The standard production use: a test or sequence pins one field to a directed value while the rest of the transaction stays random — directed-random stimulus. Compare the mechanisms: an inline with { addr == X } also pins the value, but rand_mode(0) keeps the pin across many randomize() calls without repeating the constraint, which is exactly what loops want.

systemverilog
class axi_txn;
  rand bit [31:0] addr;
  rand bit [7:0]  len;
  rand bit        write;
  constraint legal_c { len inside {[1:16]}; addr[1:0] == 0; }
endclass

task hammer_one_address(axi_txn t, bit [31:0] target, int n);
  t.addr = target;          // 1. set the directed value
  t.addr.rand_mode(0);      // 2. freeze it (it must satisfy addr[1:0]==0!)
  repeat (n)
    if (!t.randomize())     // 3. len/write re-randomized each iteration
      $fatal(1, "frozen addr violates legal_c");
  t.addr.rand_mode(1);      // 4. ALWAYS restore for the next user
endtask
diagram
rand_mode(0) — WHAT CHANGES AT SOLVE TIME

  rand fields:   addr      len      write
                  │          │        │
  rand_mode:      0          1        1
                  │          │        │
                  ▼          ▼        ▼
              read as     SOLVED   SOLVED
              STATE         ▲        ▲
              (=0xA000)     │        │
                  │     constraints still connect them all:
                  └────► len inside {[1:16]}; addr[1:0]==0;
                         addr participates as the constant 0xA000
                          addr[1:0]==0 must HOLD or randomize()==0

Interview angle

What interviewers ask

  • “After x.rand_mode(0), do constraints on x still apply?” — yes; x becomes a state variable and constraints on it become checks against its frozen value. The screening question for this feature.

  • “Why did randomize() start failing after I froze a field?” — the frozen (often default-zero) value violates an active constraint; set a legal value before freezing.

  • “rand_mode(0) vs inline with { x == k }?” — both pin x for a call; rand_mode persists across calls and needs no constraint repetition, but is sticky state you must restore.

  • “How do you check whether a field is currently randomized?” — the function form x.rand_mode() returns the mode; only valid per variable.

  • “Does object-level rand_mode(0) disable constraints?” — no; it freezes variables. Disabling constraints is constraint_mode's job — a deliberately confusable pair.

Key takeaways

  • rand_mode(0) converts a rand field into a state variable — constraints on it still apply.

  • Set a legal value before freezing; fields default to zero and zero often violates constraints.

  • Per-field and per-object forms exist; the query form is per-variable only.

  • Mode is sticky instance state — restore rand_mode(1) after directed phases.

  • Use rand_mode for multi-call pins; use inline with for one-call pins.

Common pitfalls

  • Freezing a field at its default 0 and hitting mysterious randomize() failures.

  • Assuming rand_mode(0) disables constraints on the field — it does not; constraint_mode does that.

  • Forgetting to restore rand_mode(1), silently freezing the field for all later tests sharing the object.

  • Calling p.rand_mode() as an object-level query — illegal; query is per variable.

  • Using rand_mode on a randc field then expecting the cycle to resume where it left off — the cycle state interaction is tool-dependent; re-check after thawing.