Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
    • Randomization Basics: rand, randc & randomize()
      • rand vs randc
      • The randomize() Call
      • Hooks and Nested Objects
      • Random Stability and Seeding
    • Constraint Operators & Expressions
    • Dynamic Constraint Control
    • Randomizing Arrays and Queues
    • How the Constraint Solver Works
    • Debugging Randomization Failures
    • Interview Drills — Constraints and Randomization Q&A
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Randomization Basics: rand, randc & randomize()
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Randomization Basics: rand, randc & randomize()
  5. rand vs randc

Part 3 · Constraint Randomization · Intermediate

rand vs randc

Uniform rand draws versus cyclic randc permutations, periods, limits and slot burning.

On this page

    Why this matters

    A sweep needed ids 0 to 3 once each and sent 1, 0, 2, 1. The randc field was fine; the test re-randomized the whole object for another field and burned an id.

    After this lesson you can: choose rand or randc for a field, predict a randc period, and avoid the re-randomize habit that burns randc values.

    Two sampling contracts

    Both qualifiers hand a field to the solver, with different promises. rand means an independent uniform draw each call, so repeats are normal.

    randc means random-cyclic: the solver shuffles all legal values into a permutation and deals one per call. It reshuffles only when the permutation is exhausted.

    Predict first Eight calls deal values from a 2-bit randc field. Calls 4 and 5 both return 2. Is the solver broken?

    No. Call 4 ends one permutation and call 5 starts a new, independent one, so the two can match. A repeat is impossible only inside a cycle of four.

    The listing below makes eight calls and prints both fields. It checks that c deals 0 to 3 once in each block of four calls, and counts how often r repeats back to back.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class demo;
      rand  bit [1:0] r;   // 4 legal values, independent uniform picks
      randc bit [1:0] c;   // 4 legal values, dealt as a random permutation
    endclass
    
    module tb_rand_randc;
      demo d = new();
      bit [3:0] dealt;
      int repeats_r = 0;
      bit [1:0] last_r;
      initial begin
        for (int call = 1; call <= 8; call++) begin
          if (!d.randomize()) $fatal(1, "FAIL: randomize failed");
          $display("call %0d: r=%0d  c=%0d", call, d.r, d.c);
          if (call > 1 && d.r == last_r) repeats_r++;
          last_r = d.r;
          if (call % 4 == 1) dealt = 0;
          if (dealt[d.c]) $fatal(1, "FAIL: c repeated %0d inside one cycle", d.c);
          dealt[d.c] = 1;
          if (call % 4 == 0 && dealt != 4'hF) $fatal(1, "FAIL: a cycle of 4 missed a value");
        end
        $display("PASS: c dealt 0..3 once in each block of 4 calls; r repeated back to back %0d times", repeats_r);
        $finish;
      end
    endmodule
    Terminal
    call 1: r=3  c=2
    call 2: r=1  c=1
    call 3: r=2  c=3
    call 4: r=1  c=0
    call 5: r=1  c=1
    call 6: r=1  c=3
    call 7: r=1  c=0
    call 8: r=0  c=2
    PASS: c dealt 0..3 once in each block of 4 calls; r repeated back to back 3 times

    The extra randc bookkeeping explains its limits. The solver must remember which values of the current permutation are used, which is why randc has vendor-dependent width limits and constraint restrictions.

    Aspect

    rand

    randc

    Promise

    Independent uniform draw each call

    One random permutation dealt out, then reshuffled

    Repeats

    Expected at any time

    Never within a cycle; possible at a boundary

    Solver state

    None kept between calls

    Used-value set per variable per object

    Restrictions

    None

    Solved before rand; no dist weights; width limit is vendor-dependent

    Use

    General stimulus

    Cover-each-value-once sweeps inside one object

    The randc period

    A randc field deals a fresh permutation every period; only the boundary can repeat.

    The period counts legal values, not representable ones. A constraint that restricts a 4-bit randc field to 5 values makes the cycle length 5.

    Back-to-back duplicates can occur exactly at a cycle boundary. The new permutation is independent of the old one, so the last value of one cycle may equal the first of the next.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class limits;
      randc bit [7:0] small_id;  // narrow randc: portable
      rand  bit [7:0] data;
    
      // ILLEGAL: dist on randc contradicts the exactly-once contract
      // constraint c_bad { small_id dist { 0 := 9, [1:255] :/ 1 }; }
    
      // Legal: inside restricts the cycle to 5 values, so the period is 5
      constraint c_set { small_id inside {10, 20, 30, 40, 50}; }
    endclass
    
    module tb_randc_set;
      limits l = new();
      int seen[int];
      initial begin
        for (int call = 0; call < 20; call++) begin
          if (call % 5 == 0) seen.delete();
          if (!l.randomize()) $fatal(1, "FAIL: randomize failed");
          if (!(l.small_id inside {10, 20, 30, 40, 50})) $fatal(1, "FAIL: %0d is outside the set", l.small_id);
          if (seen.exists(int'(l.small_id))) $fatal(1, "FAIL: %0d repeated inside a cycle of 5", l.small_id);
          seen[int'(l.small_id)] = 1;
        end
        $display("PASS: 20 calls made 4 full cycles, each dealing 10, 20, 30, 40 and 50 once");
        $finish;
      end
    endmodule
    Terminal
    PASS: 20 calls made 4 full cycles, each dealing 10, 20, 30, 40 and 50 once

    The solver builds the permutation only over the inside set. Each group of five consecutive calls then yields those five values in some order.

    The standard lets implementations cap randc width, but the cap must be at least 8 bits (IEEE 1800 18.4.2). Wider limits are vendor-dependent, so keep randc fields narrow and shuffle an explicit list for wide spaces.

    Where randc misleads

    The guarantee is per object, per variable, across consecutive calls of that object. Three situations quietly break the all-values-once expectation that people reach for randc to get.

    • Re-randomizing for other fields still advances the randc cycle. Every call consumes one permutation slot, even if only another field mattered.

    • Two objects hold independent cycle state. Ten packets with a randc id each cover the range separately, never collectively.

    • Constraints that change mid-cycle restart the cycle. IEEE 1800 18.4.2 recomputes the permutation when the constraints on the variable change, so a value can repeat before every value was used.

    top.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // snippet: send() stands for driving the packet
    class pkt;
      randc bit [1:0] id;     // intent: use each id once before repeating
      rand  bit [7:0] data;
    endclass
    
    module top;
      pkt p = new();
      initial begin
        // Intent: 4 packets, ids 0..3 each exactly once. Looks right...
        repeat (4) begin
          if (!p.randomize()) $error("randomize failed");
          if (p.data == 0) begin
            if (!p.randomize()) $error("randomize failed");  // ALSO consumes an id slot
          end
          send(p);
        end
        $finish;
      end
    endmodule
    // Sent ids may read e.g. 1,0,2,1: a value repeated and one missing.
    // The cycle itself never repeated; one of its elements was discarded.

    The solver cannot know a result was discarded. Each call deals the next permutation element, so the re-roll burns one id. The fix is a field list from the next lesson: p.randomize(data) re-rolls only data and leaves id untouched.

    Testbench

    The interview sweep below runs in this course's checks. A randc field with no constraints needs no constraint solver, so Verilator runs it. It asserts that each 16-call window is a permutation. An explicit shuffle fallback for wide fields follows.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class walker;
      randc bit [3:0] val;   // 16 calls deal 16 distinct values, random order
    endclass
    
    module tb_walker;
      walker w = new();
      bit seen[16];
      initial begin
        repeat (2) begin                       // two full cycles of 16 calls
          foreach (seen[i]) seen[i] = 0;
          repeat (16) begin
            if (!w.randomize()) $fatal(1, "FAIL: randomize failed");
            if (seen[w.val]) $fatal(1, "FAIL: value %0d repeated inside one cycle", w.val);
            seen[w.val] = 1;
          end
          foreach (seen[i]) if (!seen[i]) $fatal(1, "FAIL: value %0d missing from the cycle", i);
        end
        $display("PASS: two 16-call randc cycles each dealt all 16 values once");
        $finish;
      end
    endmodule
    Terminal
    PASS: two 16-call randc cycles each dealt all 16 values once
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: use_value() stands for whatever consumes each value
    // Wide-field alternative: explicit shuffle, no randc width limit.
    task automatic drive_wide();
      int unsigned vals[];
      vals = new[1024];
      foreach (vals[i]) vals[i] = i;
      vals.shuffle();                       // random permutation, exactly once each
      foreach (vals[i]) use_value(vals[i]);
    endtask

    Common pitfall

    Re-randomizing a whole object to fix one field burns a randc slot, so sent ids can repeat and skip values while the cycle itself never repeats. Re-roll with a field list instead.

    Interview

    The canonical question is generating all values of a field exactly once in random order. The expected first answer is randc, then the limits before being asked.

    • Two consecutive randc values can be equal exactly at a cycle boundary, never within a cycle.

    • dist on a randc variable is a compile error: weighting contradicts the once-per-cycle contract.

    • randc variables are solved before rand variables through an implicit ordering.

    • Two objects never share a randc cycle: state is per variable per object.

    Check yourself Ten packets each have their own randc bit [3:0] id. Do the ten packets together cover 0 to 15?

    Not necessarily. Each object keeps its own cycle, so ten objects each start their own permutation; nothing coordinates them. One object called sixteen times covers 0 to 15.

    Check yourself A 32-bit field must take each of 1024 values exactly once. Why is randc the wrong tool?

    The LRM only guarantees randc widths up to 8 bits; wider limits are vendor-dependent. Shuffling an explicit 1024-element list gives the same guarantee with no width risk.

    Remember

    • rand: an independent draw each call, so repeats are normal.

    • randc: a permutation of the legal values per cycle; a repeat only across a boundary.

    • Every randomize() call consumes a randc value, even when you only wanted another field.

    Rule of thumb: randc promises once per cycle per object, nothing more.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator prints a harmless WIDTHTRUNC warning for if (!obj.randomize()), because randomize() returns an int, and an UNSATCONSTR warning when a solve fails on purpose. Icarus 13 does not support constraints.

    Bench output is from a fixed seed; another seed deals a different permutation with the same property.

    Related topics

    • Next: The randomize() Call

    Previous

    65 / 259

    Next: The randomize() Call

    Up next

    Up nextThe randomize() Call

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Hooks and Nested Objects
    • Random Stability and Seeding