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

  • SystemVerilog Language Foundations34
  • Object-Oriented SystemVerilog for Verification33
  • Constraint Randomization — Interview-Critical Deep Dive56
    • Randomization Basics: rand, randc & randomize()
    • Constraint Operators & Expressions
    • Dynamic Constraint Control
    • Randomizing Arrays & Queues
      • Constraining Array Size
      • foreach Constraints
      • sum(), Aggregate Constraints
      • unique Constraints
      • Arrays of rand Objects
      • Array Constraint Cookbook
    • How the Constraint Solver Works
    • Debugging Randomization Failures
    • Constraint Architecture for Transactions
    • Interview Constraint Patterns — Write-the-Constraint Drills
    • Randomization Interview Q&A — Deep Answers
  • SystemVerilog Assertions — Interview-Critical Deep Dive54
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog32
  • Advanced Language & Integration30
  • Senior & Interview Preparation29
Loading lesson

‹Randomizing Arrays & Queues
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Randomizing Arrays & Queues
  5. unique Constraints

Part 3 · Constraint Randomization · Intermediate

unique Constraints

unique { arr } semantics, mixing scalars and arrays, generating shuffled permutations, unique vs randc, and what uniqueness costs the solver.

On this page

    unique: pairwise inequality in one keyword

    The unique constraint declares that every listed value is pairwise distinct. unique { arr }; means no two elements of arr are equal — semantically identical to writing arr[i] != arr[j] for every i ≠ j pair, but vastly more readable and easier for the solver to recognize as an all-different problem. The braces can mix scalars and arrays: every scalar and every element of every listed array must differ from all the others.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class distinct_ids;
      rand bit [3:0] id[6];
      rand bit [3:0] master_id;
      rand bit [3:0] a, b, c;
    
      constraint c {
        unique { id };                 // 6 array elements all distinct
        unique { master_id, id };      // master differs from every element too
        unique { a, b, c };            // three scalars pairwise distinct
      }
    endclass
    
    // unique { id } on bit [3:0] id[6]:
    //   16 possible values, 6 slots -> satisfiable.
    // If id were bit [2:0] id[10]:
    //   8 values, 10 slots -> PIGEONHOLE: unsatisfiable, randomize()=0.

    The pigeonhole check is the first thing to verify: the element type must offer at least as many values as there are entries in the unique set, after intersecting with any range constraints. unique combined with foreach (id[i]) id[i] inside {[0:5]} on a 6-element array leaves exactly one solution family: a permutation of 0..5.


    Generating a shuffled permutation

    That last observation is the standard interview recipe for a random permutation of 0..N-1: fix the size to N, restrict every element into [0, N-1], and require uniqueness. With N values squeezed into N distinct slots, the only legal assignments are permutations — and a conforming solver picks uniformly among them.

    top.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 perm_gen #(int N = 8);
      rand int unsigned p[];
    
      constraint c {
        p.size() == N;
        foreach (p[i]) p[i] inside {[0:N-1]};
        unique { p };
      }
    endclass
    
    module top;
      initial begin
        perm_gen #(8) g = new();
        repeat (3) begin
          void'(g.randomize());
          $display("%p", g.p);   // e.g. '{3,0,6,1,7,4,2,5}
        end
      end
    endmodule
    
    // Procedural alternative (no solver): arr.shuffle()
    //   - cheaper, but NOT constraint-aware: you cannot say
    //     "permutation where p[0] != 0" with shuffle alone.
    //   - the constraint version composes with any extra rule:
    constraint c_extra { p[0] != 0; }   // derangement-at-0 permutation

    Mention shuffle() as the cheap procedural alternative when no additional constraints apply — knowing when not to use the solver is part of the expected answer.


    unique vs randc

    Both produce non-repeating values, but on different axes. unique enforces distinctness within one randomize() call, across the listed variables/elements. randc enforces non-repetition across successive randomize() calls of one variable — it cycles through a permutation of its value space over time. They answer different questions and are not interchangeable.

    Diagram
    UNIQUE vs RANDC — TWO AXES OF NON-REPETITION
    
                     one call, many values     many calls, one value
                     (spatial)                 (temporal)
                     ----------------------    ----------------------
      keyword        unique { arr }            randc bit [3:0] v;
      guarantees     all listed values         v cycles all 16 values
                     differ in THIS solve      before any repeat
      scope          across variables/         across successive
                     elements                  randomize() calls
      resets when    every call re-solves      cycle exhausted ->
                                               new random permutation
    
      call 1:  unique arr = {3,7,1}   randc v = 5
      call 2:  unique arr = {7,7? NO  randc v = 2   (not 5)
               -> always distinct}    ...
      call 16:                        randc v = last unseen value
      call 17:                        new permutation begins
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class compare;
      rand  bit [3:0] arr[4];
      randc bit [3:0] chan;
    
      constraint c { unique { arr }; }
      // arr: each CALL gives 4 distinct values; values may repeat
      //      between calls (call1 {1,5,9,2}, call2 {5,3,1,8} is fine).
      // chan: one value per call; guaranteed to visit all 16 values
      //       across 16 calls before any repeat.
    endclass

    Solver cost of uniqueness

    unique over N elements implies on the order of N²/2 pairwise inequalities. Modern solvers special-case all-different sets, but cost still grows with N and with how tightly other constraints squeeze the value space — a unique set that exactly fills its range (the permutation case) is the hardest version, since almost every partial assignment constrains the rest. Keep unique sets as small as the requirement allows, and widen value ranges when you can.


    Interview angle

    What interviewers probe

    • "Generate a random permutation of 0..N-1" — expected: size==N, inside [0:N-1], unique; bonus for naming shuffle() as the unconstrained alternative.

    • "unique vs randc?" — expected: unique is within-one-call across elements; randc is across-calls for one variable cycling its space.

    • "What if the range is smaller than the array?" — expected: pigeonhole, unsatisfiable, randomize() returns 0 — check value-count vs slot-count first.

    • "What does unique cost?" — expected: O(N²) pairwise inequalities conceptually; tight ranges make it harder; permutation is the worst case.

    A subtle senior-level point: randc cannot participate in dist or solve...before, so when an interviewer asks for "non-repeating but weighted" values, the answer is unique-style constraints plus history kept in the class, not randc.

    Key takeaways

    • unique { ... } = pairwise distinct across all listed scalars and array elements, within one solve.

    • Permutation recipe: size == N, elements inside [0:N-1], unique — only permutations remain.

    • unique is spatial (one call), randc is temporal (across calls) — different tools for different requirements.

    • Check the pigeonhole condition before anything else: value-space size must be >= unique-set size.

    • Uniqueness cost grows roughly quadratically and worsens as ranges tighten — keep sets small.

    Common pitfalls

    • unique set larger than the available value space — silent unsatisfiability, randomize() returns 0.

    • Expecting unique to prevent repeats BETWEEN randomize() calls — that is randc territory (or manual history).

    • Using randc when distribution weighting is also needed — randc is illegal in dist; restructure with rand + constraints.

    • Reaching for the solver to shuffle an unconstrained array — arr.shuffle() is free; save the solver for constrained permutations.

    • Forgetting that range constraints shrink the value space the pigeonhole check applies to — unique passes alone, fails once inside [a:b] is added.

    Previous

    89 / 296

    Next: Arrays of rand Objects

    Up next

    Up nextArrays of rand Objects

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Constraining Array Size
    • foreach Constraints
    • Array Constraint Cookbook