SV-03

Constrained-Random
Verification in SystemVerilog

EcrioniX · SystemVerilog Series· ~17 min read· 7 Code Examples
Directed Tests vs Constrained-Random — Coverage Space
DIRECTED TESTS Write address=0x1000 Read address=0x1000 Boundary: addr=0xFFFF Manual, predictable, misses corners CONSTRAINED RANDOM Auto-generates legal stimulus Covers entire valid space automatically

Why Constrained-Random?

Writing directed tests for a 32-bit address bus requires 4 billion test cases to be exhaustive — obviously impossible. Constrained-random verification lets you define what is legal (the constraints) and let the solver pick random values within that legal space automatically. One test with 10,000 random transactions covers far more of the design space than 100 manually written directed tests.

1. rand and randc

KeywordBehaviorUse Case
randRandom each call, repeats allowedMost fields — address, data, control signals
randcCyclic — no repeat until all values seenIDs, tags where exhaustive coverage matters
SystemVerilog
class AXITxn;
  rand  bit [31:0] addr;    // random, repeats OK
  rand  bit [31:0] data;
  rand  bit [2:0]  size;    // AxSIZE: 0=byte, 1=half, 2=word
  rand  bit [7:0]  len;     // AxLEN: burst length-1
  randc bit [3:0]  id;      // cyclic IDs — exhaust all before repeat
  rand  bit         write;
endclass

initial begin
  AXITxn txn = new();

  repeat (10) begin
    // randomize() returns 1 on success, 0 on constraint failure
    assert (txn.randomize()) else $fatal("Randomize failed!");
    $display("addr=%0h len=%0d id=%0d", txn.addr, txn.len, txn.id);
  end
end

2. Constraint Blocks

Constraints are Boolean expressions the solver must satisfy when generating random values:

SystemVerilog — Constraints
class AXITxn;
  rand bit [31:0] addr;
  rand bit [2:0]  size;   // 0-4 legal (byte to 16B)
  rand bit [7:0]  len;

  // Address must be naturally aligned to transfer size
  constraint c_align {
    addr[2:0] == 3'b0 >> (3 - size);  // alignment
    addr < 32'h8000_0000;              // only low half of address space
  }

  // Burst length: AXI4 allows up to 256 beats
  constraint c_burst {
    len inside {[0:15]};   // short bursts for most tests
    size inside {0,1,2};   // byte, halfword, word only
  }

  // Weighted distribution: 70% writes, 30% reads
  rand bit write;
  constraint c_rw {
    write dist {1 := 70, 0 := 30};
  }
endclass

3. Constraint Operators

OperatorSyntaxExample
insidex inside {a, b, [c:d]}len inside {[1:15]}
distx dist {v1 := w1, v2 := w2}write dist {1:=70, 0:=30}
dist :/x dist {[a:b] :/ w}addr dist {[0:'hFF]:/50, [0:'hFFFF]:/50}
implicationif (cond) {constraint}if (write) {data != 0}
solve beforesolve a before bsolve mode before len
uniqueunique {a, b, c}unique {id1, id2, id3}

4. Inline Constraints — Overriding Per-Call

Override constraints for a specific randomize() call without modifying the class:

SystemVerilog — Inline constraints
AXITxn txn = new();

// Normal random
assert (txn.randomize());

// Force a specific write to 4KB boundary — inline override
assert (txn.randomize() with {
  write == 1;
  addr[11:0] == 0;   // 4KB-aligned address
  len == 15;           // max 16-beat burst
});

// Temporarily disable a constraint
txn.c_burst.constraint_mode(0);   // disable c_burst
assert (txn.randomize() with { len inside {[100:255]}; });
txn.c_burst.constraint_mode(1);   // re-enable

5. Soft Constraints — Overridable Defaults

A soft constraint is a preference the solver honors unless a harder constraint from outside contradicts it:

SystemVerilog
class AXITxn;
  rand bit [7:0] len;

  // Default: prefer short bursts — but tests can override
  constraint c_default_len {
    soft len inside {[0:15]};
  }
endclass

// Most tests: len will be 0..15 (soft constraint wins)
txn.randomize();

// Stress test: force long bursts — inline overrides the soft
txn.randomize() with { len inside {[200:255]}; };  // soft dropped

6. pre_randomize and post_randomize

SystemVerilog
class AXITxn;
  rand bit [31:0] addr;
  rand bit [7:0]  len;
  bit             is_last_beat;   // derived field

  // Called BEFORE solver — enable corner-case mode if needed
  function void pre_randomize();
    if ($urandom_range(0,9) == 0)     // 10% chance
      c_burst.constraint_mode(0);     // remove burst limit for stress
  endfunction

  // Called AFTER solver — compute fields the solver can't express
  function void post_randomize();
    c_burst.constraint_mode(1);       // always restore
    is_last_beat = (len == 0);         // derived from randomized len
    addr[1:0] = 2'b00;                  // force word-align after solve
  endfunction

  constraint c_burst { len inside {[0:63]}; }
endclass

7. Seed Control — Reproducibility

SystemVerilog + Simulator CLI
// Print seed at start so you can replay any failing run
initial begin
  int seed;
  seed = $get_initial_random_seed();
  $display("[SEED] %0d", seed);
end

// Per-object seed (for independently reproducible sub-components)
txn.srandom(42);      // fix this object's RNG to seed 42

// Simulator CLI seeds (to replay a specific run):
// VCS:    vcs +ntb_random_seed=12345
// Questa: vsim +seed=12345
// Xcelium: xrun -seed 12345
🔗
Next: Functional Coverage (SV-04) — now that you can generate random stimulus, measure which design behaviors have been exercised using covergroups and coverpoints.