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()
    • Constraint Operators & Expressions
      • inside, Ranges & Set Membership
      • dist: Weighted Distributions
      • Implication, if/else and solve...before
      • soft Constraints
    • 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

‹Constraint Operators & Expressions
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Constraint Operators & Expressions
  5. dist: Weighted Distributions

Part 3 · Constraint Randomization · Intermediate

dist: Weighted Distributions

Weights with dist, per-value versus per-range shares and renormalization.

On this page

    Why this matters

    A rare error was meant for about 1 packet in 20 and turned up in 83% of them. The weight was written with :/ where := was meant, and one character moved most of the probability.

    After this lesson you can: write dist with := and :/, work out each value's probability from the weights, and predict what happens when another constraint removes a value.

    dist constrains and weights

    The dist operator does two jobs at once. It constrains the variable to the listed set, and it weights the pick so the solver no longer aims at uniform.

    Each item probability is its weight over the sum of all weights. Weights are relative, so {a := 1, b := 3} and {a := 25, b := 75} describe the same split.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet: the weight arithmetic, measured in the bench below
    class arb_txn;
      rand bit [1:0] grant;
    
      // grant is 0, 1 or 2 only (3 is now ILLEGAL: dist constrains the set)
      constraint grant_c {
        grant dist { 0 := 1, 1 := 2, 2 := 5 };
      }
    endclass
    // Total weight = 1 + 2 + 5 = 8
    // P(grant==0) = 1/8, P(grant==1) = 2/8, P(grant==2) = 5/8
    // P(grant==3) = 0: not in the dist set, never produced

    The trap is the constraining half. Values outside the list vanish entirely, and a clash with another constraint fails randomize.

    Per-value and per-range weights

    On a single value, := and :/ behave identically. On a range they differ: := gives the weight to each value, while :/ gives it to the range as a whole.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // snippet: the weight arithmetic, measured in the bench below
    class dist_cases;
      rand bit [7:0] xa;
      rand bit [7:0] xb;
    
      // Case A: := on a range is weight PER VALUE (range total 50, sum 100)
      constraint a_c { xa dist { [1:5] := 10, 9 := 50 }; }
    
      // Case B: :/ on a range splits the weight (range total 10, sum 60)
      constraint b_c { xb dist { [1:5] :/ 10, 9 := 50 }; }
    endclass
    // Case A: P(x==1) = 10/100 = 10% each of 1..5; P(x==9) = 50/100 = 50%
    // Case B: P(x==1) = 2/60, about 3.3% each of 1..5; P(x==9) = 50/60, about 83.3%
    := gives the weight to every value of the range; :/ shares it among them.

    A usable mnemonic is that := weights each member and :/ divides across the range. Recompute the total before quoting any probability when a range is present.

    Intersections renormalize the weights

    Other constraints intersect with dist as usual. The surviving weights renormalize within the final legal set, so every remaining probability shifts.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet: the weight arithmetic, measured in the bench below
    class err_txn;
      rand bit [3:0] resp;
    
      // 0=OKAY with most weight, 1..3 share the rest
      constraint resp_c {
        resp dist { 0 :/ 90, [1:3] :/ 10 };
      }
    endclass
    // A test adds resp != 2, which removes value 2.
    // Surviving weights: 0 -> 90, 1 -> 10/3, 3 -> 10/3, total about 96.67.
    // P(resp==0) = 90/96.67, about 93.1%: renormalized, no longer exactly 90%

    Note that dist applies only to rand variables. It is illegal on randc, whose cycling already dictates the distribution.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: the weight arithmetic, measured in the bench below
    class weight_demo;
      rand bit [7:0] v;
      constraint q_c { v dist { 1 := 40, [2:4] := 60, [5:6] :/ 40 }; }
    endclass
    // Expand: 1 -> 40; 2,3,4 -> 60 EACH (:= is per value); 5,6 -> 20 each (:/ splits).
    // Total = 40 + 180 + 40 = 260.
    // P(1) = 40/260, about 15.4%; P(2..4) = 60/260 each; P(5..6) = 20/260 each.
    // All other values of v have probability 0: dist constrains the set.

    Step

    Routine for exact shares

    Expand

    Turn each range into per-value weights using the := versus :/ rule.

    Sum

    Add every item weight into one total.

    Divide

    Quote each probability as item weight over that total.

    Common pitfall

    A wide range with := silently dominates the total. A dist list also deletes unlisted values instead of merely disfavoring them.

    Interview

    Demonstrate with numbers: expand the ranges, sum the weights, then divide. Also state that dist never applies to randc.

    Check yourself What is the difference between := and :/ on the range [1:5] with weight 10?

    := gives 10 to each of the five values, for a range total of 50. :/ gives 10 to the range as a whole, so 2 per value.

    Check yourself After a dist is in place, a new constraint removes one listed value. Do the other probabilities stay the same?

    No. The surviving weights renormalize over the smaller set, so each remaining probability moves.

    Testbench

    Predict first xb dist { [1:5] :/ 10, 9 := 50 } is solved 2,500 times. About how often is xb 9?

    About 83% of the time, roughly 2,080 of 2,500. With :/ the range shares its 10, so the total is 60 and 9 holds 50 of it.

    Each dist from this lesson, solved 2,500 times, with the measured share printed beside the weight arithmetic.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class dist_cases;
      rand bit [1:0] grant;
      rand bit [7:0] xa, xb;
      rand bit [3:0] resp;
      constraint grant_c { grant dist { 0 := 1, 1 := 2, 2 := 5 }; }        // 3 is now illegal
      constraint a_c { xa dist { [1:5] := 10, 9 := 50 }; }                   // := weight per value
      constraint b_c { xb dist { [1:5] :/ 10, 9 := 50 }; }                   // :/ weight split over the range
      constraint r_c { resp dist { 0 :/ 90, [1:3] :/ 10 }; }
    endclass
    
    Terminal
    grant == 0   (1/8)         measured  12.0%   predicted  12.5%
    grant == 2   (5/8)         measured  61.9%   predicted  62.5%
    xa == 9, := (50/100)       measured  49.3%   predicted  50.0%
    xb == 9, :/ (50/60)        measured  84.2%   predicted  83.3%
    resp == 0    (90/100)      measured  89.6%   predicted  90.0%
    resp == 0 with resp != 2   measured  90.0%   standard   93.1%
    PASS: the five plain shares are within 2.5 points of the weight arithmetic, and the removed value never appeared

    The last line shows the one place this simulator drifts: after resp != 2 removes a value, the standard renormalizes the remaining weights to give 93.1% for resp == 0, and the measured share stays near 90%.

    Project step

    Shape PacketFIFO traffic: mostly DATA packets, some CTRL, rare ERR, about 85, 12 and 3 percent. Measure the shares over 3,000 packets.

    One way to write it:

    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
    27
    typedef enum bit [1:0] {DATA = 2'd0, CTRL = 2'd1, ERR = 2'd2} kind_e;
    
    class pkt_txn;
      rand kind_e    kind;
      rand bit [3:0] len;
      constraint c_len  { len inside {[1:8]}; }
      // Mostly data, some control, rare errors: 85 / 12 / 3 percent.
      // Written with the numeric values 0, 1, 2 (DATA, CTRL, ERR); see the evidence note.
      constraint c_kind { kind dist { 0 := 85, 1 := 12, 2 := 3 }; }
    endclass
    
    module tb_pkt_kind_dist;
      pkt_txn p = new();
      int k[3];
      int n = 3000;
      initial begin
        repeat (n) begin
          if (!p.randomize()) $fatal(1, "FAIL: randomize failed");
          k[p.kind]++;
        end
        $display("DATA %0d  CTRL %0d  ERR %0d  of %0d", k[DATA], k[CTRL], k[ERR], n);
        if (k[ERR] < 50 || k[ERR] > 130) $fatal(1, "FAIL: ERR share %0d is far from 3%%", k[ERR]);
        if (k[DATA] < 2460 || k[DATA] > 2640) $fatal(1, "FAIL: DATA share %0d is far from 85%%", k[DATA]);
        $display("PASS: kinds came out near 85 / 12 / 3 percent");
        $finish;
      end
    endmodule
    Terminal
    DATA 2527  CTRL 384  ERR 89  of 3000
    PASS: kinds came out near 85 / 12 / 3 percent

    Remember

    • dist limits the set to the listed values and weights the pick.

    • := gives the weight to each value; :/ shares it across a range.

    • Probability = item weight / total weight, after expanding every range.

    Rule of thumb: expand, sum, divide: never quote a dist share without the total.

    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.

    Measured on 8,000 and again on 2,500 solves: the five plain shares were within 1.5 points of the weight arithmetic. With resp != 2 added, Verilator gave about 90% for resp == 0 where renormalizing gives 93.1%.

    Verilator 5.048 ignored dist weights written with enum names (DATA := 85): every name came out about equally often. The project writes the numeric values 0, 1 and 2, which it weighted correctly.

    Related topics

    • Next: Implication, if/else and solve...before

    Previous

    70 / 259

    Next: Implication, if/else and solve...before

    Up next

    Up nextImplication, if/else and solve...before

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • soft Constraints