Skip to content

SystemVerilog · Module 10

Inline Constraints — the with Clause

Per-call constraint additions inside randomize() with { … }, scope and local-variable capture, the AND-not-substitute combination with class constraints, and the three-sites refactor signal.

Module 10 · Page 10.5

What the with Clause Does

The with clause is an anonymous constraint block appended directly to a randomize() call. It is active for that one call only and discarded immediately after. The class definition is never modified.

This is the right tool whenever you need a specific value range for a single test scenario — a corner-case transaction, a directed stimulus amid a random test — without creating a subclass or temporarily toggling constraint modes.

SystemVerilog — with clause syntax
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class Packet;
    rand bit [7:0]  src;
    rand bit [7:0]  dst;
    rand bit [15:0] len;
 
    constraint valid_len { len inside {[64:1518]}; }
endclass
 
Packet p = new();
 
// ── Normal call — only class constraints apply ─────────────────
assert(p.randomize());                    // len: 64–1518, src/dst: free
 
// ── With clause — adds an extra constraint for THIS call only ──
assert(p.randomize() with { src == 8'hA0; });  // src fixed to 0xA0
// valid_len still active — len stays in 64–1518
// after this call: src is free again on the next randomize()
 
// ── Multiple inline expressions ───────────────────────────────
assert(p.randomize() with {
    src  == 8'hA0;
    dst  != src;
    len  <= 128;
});

Scoping — What Variables You Can Reference

Inside a with { } block you can reference:

Class properties

Any rand, randc, or plain property of the object being randomised. This is the most common use.

Local variables in scope

Variables visible at the call site — local task/function variables, loop variables, module-level variables. They are treated as constants by the solver.

Parameters and constants

Any compile-time constant, parameter, or enum value accessible at the call site.

SystemVerilog — referencing local variables in with clause
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
task automatic send_to_region(
    input bit [31:0] base,
    input bit [31:0] size
);
    MemAccess m = new();
 
    // 'base' and 'size' are local task args — used as constants in with
    assert(m.randomize() with {
        addr >= base;
        addr <  base + size;
    });
 
    // drive_bus(m); ...
endtask
 
// Call with different regions each time — no class changes needed
send_to_region(32'h0000_0000, 32'h0001_0000);
send_to_region(32'h4000_0000, 32'h0010_0000);

How with Combines With Class Constraints

The full constraint set the solver sees on every call is:

SourceActive whenScope
Class constraint blocksAlways, unless turned off via constraint_mode(0)Every randomize() call on this object
Inherited parent constraintsAlways, unless overridden by same name in childEvery randomize() call on this object
with { } inline constraintOnly the single call it appears onThat one call only — discarded after
SystemVerilog — class constraint + inline constraint combined
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class BusTxn;
    rand bit [31:0] addr;
    rand bit [7:0]  data;
    rand bit         write;
 
    constraint aligned { addr[1:0] == 2'b00; }          // always active
    constraint in_range { addr inside {[32'h4000_0000:32'h4FFF_FFFF]}; }
endclass
 
BusTxn t = new();
 
// Call 1 — force a write to a specific sub-region for one call
assert(t.randomize() with {
    write == 1;
    addr  inside {[32'h4000_0000:32'h400F_FFFF]};  // narrows in_range
});
// Solver satisfies: aligned AND in_range AND write==1 AND sub-region
 
// Call 2 — back to normal random, no inline constraint
assert(t.randomize());
// Solver satisfies: aligned AND in_range only

with Combined With randomize() Argument List

You can combine a selective argument list (from page 10.3) with a with clause in one call. The argument list restricts which fields are randomised; the with block constrains the values.

SystemVerilog — argument list + with clause combined
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
BusTxn t = new();
 
// Randomise only 'data', constrain it to non-zero, keep addr/write unchanged
assert(t.randomize(data) with { data != 8'h00; });
 
// Randomise only 'addr', keep it in a tighter sub-range for this call
assert(t.randomize(addr) with {
    addr inside {[32'h4000_0000:32'h4000_FFFF]};
    addr[1:0] == 2'b00;  // word-aligned (redundant with class constraint, harmless)
});

with on std::randomize()

std::randomize(), introduced in page 10.3, has no class constraints to draw on — the with clause is its only source of constraints. This makes it ideal for quick one-off constrained values in tasks and functions without any class overhead.

SystemVerilog — std::randomize() with inline constraints
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
task automatic inject_delay(input int min_ns, max_ns);
    int delay_ns;
 
    assert(std::randomize(delay_ns) with {
        delay_ns >= min_ns;
        delay_ns <= max_ns;
        delay_ns % 10 == 0;   // must be a multiple of 10 ns
    }) else $fatal(1, "delay randomization failed");
 
    #(delay_ns * 1ns);
endtask
 
// Generate a delay between 50–200 ns, in 10 ns steps
inject_delay(50, 200);

Practical Patterns

Pattern 1 — Corner-case stimulus in a random test

Run 99 fully random transactions, then inject one deliberate corner case — all using the same class, no subclassing needed.

SystemVerilog — corner case injection with with clause
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Packet p = new();
 
// 99 fully random packets
repeat (99) begin
    assert(p.randomize());
    drive(p);
end
 
// 1 deliberate maximum-size packet
assert(p.randomize() with { len == 1518; });
drive(p);
 
// 1 deliberate minimum-size packet
assert(p.randomize() with { len == 64; });
drive(p);

Pattern 2 — Loop-driven parameter sweep

Use a loop variable as a constant inside with to systematically sweep through a set of values while keeping all other fields random.

SystemVerilog — parameter sweep using loop variable in with
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
BusTxn t = new();
 
// Test every burst length (0–7) with random addresses
for (int burst = 0; burst < 8; burst++) begin
    assert(t.randomize() with {
        burst_len == burst;    // loop var used as constant — solver sees it as fixed
    });
    drive(t);
end

Pattern 3 — Narrowing without a subclass

Instead of creating a ShortPacket extends Packet class just to test short frames, use with for that one test sequence. Subclasses are better when the constraint is permanent and reused across many tests; inline is better when it is one-off.

SystemVerilog — inline narrowing vs subclassing
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ✓ Use with for a one-off scenario — no new class needed
assert(p.randomize() with { len <= 128; });
 
// ✓ Use a subclass when the constraint defines a reusable test object
class ShortPacket extends Packet;
    constraint short { len <= 128; }   // used across 20 different test files
endclass

Common Pitfalls

Contradicting a class constraint

If the inline constraint conflicts with an active class constraint, the solver fails and returns 0. Either disable the class constraint first with constraint_mode(0) or make the inline constraint compatible.

Using the handle prefix inside with

p.src == 0 inside a with block is illegal. Property names are unqualified inside the block. Write src == 0.

Expecting with to persist

The inline constraint applies to exactly one call. The very next randomize() without a with clause operates under class constraints only. Nothing carries over.

Overconstraining by accident

Adding an inline constraint on a field that a class constraint already pins tightly can leave no valid solution. Always reason about the intersection of both constraint sets before writing a tight inline constraint.

Inline vs Class Constraint — Decision Table

SituationUse
Constraint applies to every test that uses this classClass constraint block
Constraint applies to one specific test scenariowith inline
Constraint is used in 3+ test filesSubclass with a named constraint block
Corner-case value injected once among random trafficwith inline
Need to turn the constraint on/off across many callsNamed class constraint + constraint_mode() (page 10.6)
Quick random value in a task with no classstd::randomize(var) with { ... }

Quick Reference

SystemVerilog — with clause cheat sheet
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Basic form ────────────────────────────────────────────────
assert(obj.randomize() with { <expr>; });
 
// ── Multiple inline expressions (all AND'd) ───────────────────
assert(obj.randomize() with { expr1; expr2; expr3; });
 
// ── With argument list (selective randomisation) ──────────────
assert(obj.randomize(field_a) with { field_a > 10; });
 
// ── Properties are unqualified — no handle prefix ─────────────
// CORRECT:  with { src == 0; }
// WRONG:    with { p.src == 0; }  ← compile error
 
// ── Local variables are treated as constants ──────────────────
assert(obj.randomize() with { addr >= local_base; });
 
// ── std::randomize() with inline constraint ───────────────────
assert(std::randomize(x) with { x inside {[1:100]}; });
 
// ── Combined with class constraints ───────────────────────────
// with block is AND'd with all active class constraint blocks
// active for THIS call only — no persistence

Verification Usage — Where Inline with Earns Its Place in Real UVM

Every UVM environment uses inline with in three predictable places. Outside these three, the inline form usually points at a refactor opportunity — most often "promote to a derived class."

Inside ``uvm_do_with` macros in sequences

The most common use: sequences narrow the legal space per call using the inline form. The base transaction stays generic, the sequence chooses the scenario.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class apb_burst_seq extends uvm_sequence #(apb_xact);
  task body();
    repeat (8) begin
      `uvm_do_with(req, {
        pwrite == 1;
        paddr inside {[32'h4000_0000 : 32'h4000_00FF]};
      })
    end
  endtask
endclass</code>

Per-call corner-case stimulus inside a directed-random hybrid

A "directed-random" pattern is a test that drives some specific transactions deliberately then fills the rest with random stimulus. The deliberate transactions use inline with to pin specific fields while letting others vary.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>// Drive a specific corner case, then resume random
if (!tx.randomize() with { paddr == 32'h0; pwdata == 32'hFFFF_FFFF; })
  `uvm_fatal("CORNER", "boundary case infeasible — check class constraints")
drive(tx);
// ... return to fully random ...</code>

Test-layer relaxations through inline negation

An error-injection test can negate a class constraint inline (when the constraint is soft) to deliberately generate illegal stimulus and verify the DUT's response.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>// Class has: constraint c_align_soft { soft addr[1:0] == 2'b00; }
// Error test forces unaligned:
if (!tx.randomize() with { addr[1:0] != 2'b00; })
  `uvm_fatal("ERR", "could not generate unaligned address")</code>

Simulation Behavior — What the Solver Actually Does With with

Constraint set assembly with inline expressions

At randomize() call time, the solver collects (a) every enabled class constraint block from every class in the inheritance chain, and (b) every expression in the inline with clause. All are AND-ed into a single conjunction. Local procedural variables referenced in the with clause are captured by reference; their values at call time are snapshotted as constants.

No precedence between inline and class constraints

The solver treats every expression equally — there is no "class constraints win" or "inline overrides class." If the conjunction is satisfiable, every constraint holds in the solution. If not, the call returns 0. The mental model "inline overrides class" is a frequent source of confusion: inline adds, never replaces. To remove a class constraint, you must call constraint_mode(0) on it before the randomize call.

Soft constraint interaction (preview of 10.7)

If a class constraint is declared soft, the solver treats it as a preference rather than a requirement: when the inline with contradicts it, the soft constraint is dropped (only that conflicting one), and the solver finds a solution satisfying the inline plus all hard class constraints. This is how protocol-relaxing error tests work without explicit constraint_mode manipulation — the class constraint is soft, the inline says "not aligned," and the soft yields.

Cost characteristics

Adding inline expressions has roughly the same cost as adding equivalent class constraints — the solver doesn't distinguish provenance. A complex inline with with foreach loops or implication chains can be just as solver-expensive as the same expressions in a class block.

✅ Inline AND class jointly satisfiable
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [31:0] addr;
  constraint c_align { addr[1:0] == 2'b00; }
endclass
 
// Inline says addr in [0:7], class says [1:0]==0
// Joint solution: addr ∈ {0, 4}
tx.randomize() with { addr inside {[0:7]}; };</code>
❌ Inline contradicts class — solve fails
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [31:0] addr;
  constraint c_align { addr[1:0] == 2'b00; }
endclass
 
// Inline says addr is 1 (unaligned)
// Class says addr must be aligned
// Joint solution: none → randomize() = 0
tx.randomize() with { addr == 1; };</code>

Waveform Analysis — Tracing Where Inline Constraints Came From

Inline constraints exist only at the call site; they do not appear in the class definition and cannot be queried at runtime. The way to make them traceable is to log the call site explicitly alongside the constrained values.

The instrumentation pattern

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>// Inside a sequence, log which inline constraint scenario fired:
`uvm_info("SCN", "Running aligned-write scenario", UVM_HIGH)
if (!tx.randomize() with {
      pwrite == 1;
      paddr inside {[32'h4000_0000 : 32'h4000_00FF]};
    }) `uvm_fatal("SCN", "aligned-write scenario infeasible")
drive(tx);</code>

ASCII view — scenario logging alongside transaction values

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>  100ns  SCN   Running aligned-write scenario
  100ns  RAND  paddr=0x4000_0010  pwrite=1
  200ns  SCN   Running aligned-write scenario
  200ns  RAND  paddr=0x4000_0044  pwrite=1
  300ns  SCN   Running corner-case scenario
  300ns  RAND  paddr=0x0000_0000  pwrite=1   pwdata=0xFFFF_FFFF
         ↑                        ↑
   scenario name             matches the inline
   (from `uvm_info)          with clause that ran</code>

Reconciling failures back to the inline constraint that caused them

When a test fails, the post-mortem question is often "which inline with at which call site produced this transaction?" Without the scenario log, the answer requires reading every with in the sequence file. With the log, the answer is one line above the randomisation.

Industry Insights — Hard-Won Lessons About Inline with

Debugging Academy — Five Real Bugs From Inline with Misuse

1

"with { addr == 1; } always fails to solve"

DEBUG
Symptom

A test wants to drive a single transaction to address 1. tx.randomize() with { addr == 1; } returns 0 every call; UVM fatals.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [31:0] addr;
  constraint c_align { addr[1:0] == 2'b00; }   // class requires alignment
endclass
 
if (!tx.randomize() with { addr == 1; })
  `uvm_fatal("RAND", "couldn't drive addr=1");</code>
Root Cause

The class's c_align requires addr[1:0] == 2'b00. The inline clause requires addr == 1 (i.e., addr[1:0] == 2'b01). The two are jointly unsatisfiable — the inline doesn't override the class, it adds.

Fix

Two options. (a) If the test legitimately wants to violate alignment for error injection, disable the class constraint first: tx.c_align.constraint_mode(0);, then randomize. (b) If alignment is required by the protocol and the test shouldn't violate it, pick an aligned value: with { addr == 0; }. Either way, the inline form alone cannot remove a class constraint.

2

"Local variable referenced in inline with gets shadowed by class field"

DEBUG
Symptom

A test computes a local int addr_max and uses it inside with { addr inside {[0:addr_max]}; }; the constraint behaves as if addr_max is always 0xFFFF_FFFF.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [31:0] addr;
  bit [31:0] addr_max = 32'hFFFF_FFFF;   // class field with the SAME NAME
endclass
 
task body();
  xact tx = new();
  int addr_max = 32'h00FF;   // local, intended for the with clause
  if (!tx.randomize() with { addr inside {[0:addr_max]}; })
    `uvm_fatal("","");
  // addr ends up across the full 32-bit range, not [0:0x00FF]
endtask</code>
Root Cause

Inside the with body, name resolution starts in the class scope — so addr_max resolves to the class field (0xFFFF_FFFF), shadowing the local variable. The local is unused; the constraint uses the class field's value.

Fix

Either (a) qualify the local explicitly using local::addr_max (when the syntax is supported), or (b) rename the local to avoid the collision — int local_addr_max. Convention: never name a local variable the same as any field on the class being randomised.

3

"Inline with used to "freeze" a field still randomises it"

DEBUG
Symptom

A test sets tx.kind = T_READ then calls tx.randomize() with { /* no kind */ }; expecting kind to stay T_READ. After the call, kind is a different value.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand xact_kind_e kind;
  rand bit [31:0]  addr;
endclass
 
tx.kind = T_READ;
if (!tx.randomize() with { addr inside {[0:255]}; }) `uvm_fatal("","");
$display("kind=%s", tx.kind.name());  // Sometimes T_WRITE, T_ATOMIC, etc.</code>
Root Cause

The bare randomize() form (including with an inline clause that doesn't mention kind) randomises every rand field, including kind. The pre-call assignment tx.kind = T_READ is overwritten by the solve.

Fix

Use the named-argument form to limit the solve to specific fields: tx.randomize(addr) with { addr inside {[0:255]}; };. Now only addr is randomised; kind keeps its pre-call value. Alternatively, add kind == T_READ to the inline with so the solver pins it explicitly.

4

"with { addr == base; } in a loop produces the same value every iteration"

DEBUG
Symptom

A test increments base in a loop and calls tx.randomize() with { addr == base; }; the resulting tx.addr stays at the loop's first base value forever.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>int base = 0;
for (int i = 0; i < 10; i++) begin
  base = i * 16;
  if (!tx.randomize() with { addr == base; }) `uvm_fatal("","");
  // BUG: addr stuck at base's initial value? or randomized correctly?
end</code>
Root Cause

In the canonical case, this works correctly — base is snapshotted at each randomize call, so each iteration uses the current value. If you observe the field stuck, the cause is usually elsewhere: base is being shadowed by a class field, or the test is actually printing the wrong field, or the loop body has a bug. Verify by adding $display("iter=%0d base=%0d addr=%0d", i, base, tx.addr); before the next call.

Fix

Confirm the diagnostic before changing code. Inline with does correctly capture local-variable values at call time, per iteration. If the field is genuinely stuck, look for shadowing (lab 2) or a corrupted test scaffold.

5

"Inline with works alone, fails when run with the regression's verbosity settings"

DEBUG
Symptom

A test that passes locally with default verbosity fails in regression with the high-verbosity flag set. The failure points at a randomize call inside an inline with.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>// Sequence calls a UVM macro:
`uvm_do_with(req, { paddr inside {[lo:hi]}; })
 
// Verbosity-high regression also enables a tracing callback
// that mutates `lo` and `hi` during the call.</code>
Root Cause

A tracing callback or environment configuration is modifying the local variables that the with clause references — at exactly the moment the solver is reading them. Because the solver snapshots local values at call time, this is normally race-free, but a callback that fires before the snapshot can change the values that get captured. The verbosity setting was the trigger that enabled the callback.

Fix

Find the callback that mutates the local variables (grep the regression configuration for verbosity-conditional callbacks). Either disable it for this test, or copy lo and hi to fresh locals immediately before the randomize call so the callback's mutations don't affect the snapshot. Cleaner long-term: don't reference local variables in with that any callback might mutate — pass them as explicit class fields the test layer assigns before randomization.

Interview Q&A — Twelve Questions You Will Be Asked

An extension to the randomize() call that adds extra constraints for one specific call only. Syntax: obj.randomize() with { expr1; expr2; ... };. The expressions inside the braces are conjuncted with the class's existing constraints for this call only — they do not modify the class. The next call without a with clause uses only the class's built-in constraints.

Best Practices — Ten Rules for Inline with Discipline

  1. Use inline with for one-off scenarios; class constraints for recurring patterns. Three uses of the same inline = refactor signal.
  2. Always assert the randomize() return value. Inline with can fail just as silently as class constraints; the if (!...) wrapper is mandatory.
  3. Inline with never removes a class constraint — only adds. Use constraint_mode(0) or soft to relax class constraints.
  4. Use the named-argument form when you need to keep some fields fixed. tx.randomize(addr) with { ... }; randomises only the named fields.
  5. Don't shadow class fields with local variable names. Inline with resolves to the class field first; the local is silently ignored.
  6. Tag inline-with call sites with a scenario name. A short ``uvm_info` above the call makes failure logs immediately traceable to the right inline clause.
  7. Prefer ``uvm_do_withover manualrandomize() with` in sequences. The macro bundles create + randomize + send with the assertion built in.
  8. Don't use inline with as a workaround for bad class design. If twenty sequences need the same inline patch, fix the class instead.
  9. Local variables in the with clause are captured at call time. Loops with varying locals work correctly; mutations between snapshot and solve do not.
  10. Profile inline-with the same way you profile class constraints. Complex inline expressions (foreach, implication chains) can dominate runtime just as surely.