Skip to content

SystemVerilog · Module 10

Weighted Distributions — dist

Probabilistic stimulus shaping with relative weights, the := vs :/ operator semantics on ranges, the two-stage filter-then-sample model, and pairing every dist with a coverpoint.

Module 10 · Page 10.8

What dist Does

Without dist, every value in the legal range is equally probable. The dist operator lets you assign relative weights to individual values or ranges. The solver picks values in proportion to those weights — heavier items are chosen more often.

dist is a constraint expression, not a separate keyword. It appears inside a constraint block or a with clause, applied to a rand variable.

SystemVerilog — dist operator first look
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class BusTxn;
    rand bit write;
 
    // Without dist: write=0 and write=1 equally likely (50% each)
    // With dist: write=0 three times more likely than write=1
    constraint rw_ratio {
        write dist { 0 := 3, 1 := 1 };
    }
    // 75% reads (write=0), 25% writes (write=1)
endclass

Two Weight Operators — := and :/

There are two ways to assign a weight to an item inside a dist list — and they behave very differently when applied to ranges.

OperatorApplied to a single valueApplied to a range
:=Weight assigned to that one valueWeight assigned to each individual value in the range
:/Weight assigned to that one valueWeight split equally across all values in the range
SystemVerilog — := vs :/ on a range
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class WeightDemo;
    rand bit [3:0] x;
 
    // ── := assigns weight to EACH value in the range ──────────────
    // Each of 0,1,2,3,4 gets weight 10.  6 gets weight 1.
    // Total weight = 5×10 + 1 = 51
    // P(x in 0–4) = 50/51 ≈ 98%    P(x = 6) = 1/51 ≈ 2%
    constraint c_colon_eq {
        x dist { [0:4] := 10, 6 := 1 };
    }
 
    // ── :/ splits weight ACROSS the range ────────────────────────
    // Range [0:4] has 5 values, weight 10 split → each gets 10/5 = 2.
    // 6 gets weight 1.
    // Total weight = 5×2 + 1 = 11
    // P(x in 0–4) = 10/11 ≈ 91%    P(x = 6) = 1/11 ≈ 9%
    constraint c_colon_slash {
        x dist { [0:4] :/ 10, 6 :/ 1 };
    }
endclass

How Weights Translate to Probabilities

Weights are relative, not absolute percentages. The probability of any item is its weight divided by the sum of all weights. You can use any positive integers — the actual values don't matter, only their ratios.

SystemVerilog — weight-to-probability calculation
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Three values: weights 7, 2, 1 → total = 10
// P(x=0) = 7/10 = 70%
// P(x=1) = 2/10 = 20%
// P(x=2) = 1/10 = 10%
constraint traffic {
    x dist { 0 := 7, 1 := 2, 2 := 1 };
}
 
// Equivalent — only ratios matter, not the actual numbers
constraint traffic_scaled {
    x dist { 0 := 70, 1 := 20, 2 := 10 };  // same probabilities
}
 
// Weight 0 effectively excludes a value
// (same as not listing it at all)
constraint exclude_two {
    x dist { 0 := 7, 1 := 2, 2 := 0 };   // x=2 never chosen
}

Mixing Values and Ranges

A single dist list can contain any combination of individual values and ranges. Each item gets its own weight and operator.

SystemVerilog — mixed values and ranges in dist
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class PacketLen;
    rand bit [15:0] len;
 
    constraint length_dist {
        len dist {
            64          := 20,   // min-size packets: 20/57 ≈ 35%
            [65:511]   :/ 30,   // short range as a bucket: 30/57 ≈ 53%
            [512:1517] :/ 6,    // medium range as a bucket: 6/57 ≈ 11%
            1518        := 1    // max-size packets: 1/57 ≈ 2%
        };
    }
endclass
 
// Total = 20 + 30 + 6 + 1 = 57
// Corner cases (min and max) are explicitly weighted
// Ranges are treated as single buckets with :/ so range size doesn't dominate

dist Combined With Other Constraints

dist is a constraint expression — it combines with all active constraint blocks. The solver first finds the set of values that satisfies every other constraint, then applies the weighted distribution over that reduced set.

SystemVerilog — dist combined with a range constraint
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class Txn;
    rand bit [7:0] addr;
    rand bit        write;
 
    // Hard: address must be in valid peripheral space
    constraint in_range { addr inside {[8'h10:8'h7F]}; }
 
    // Weighted: within that range, bias heavily towards the base region
    constraint addr_bias {
        addr dist { [8'h10:8'h1F] :/ 60,
                     [8'h20:8'h7F] :/ 40 };
    }
 
    // Weighted: 3:1 read-heavy traffic
    constraint rw_mix {
        write dist { 0 := 3, 1 := 1 };
    }
endclass

dist in Inline with Constraints

dist works inside a with clause exactly like inside a class constraint block, making per-call traffic shaping straightforward.

SystemVerilog — dist inside with clause
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Txn t = new();
 
// Normal 3:1 read/write from the class constraint
// Override inline for a write-heavy phase
assert(t.rw_mix.constraint_mode(0));   // disable class dist
assert(t.randomize() with {
    write dist { 0 := 1, 1 := 9 };     // 90% writes inline
});
t.rw_mix.constraint_mode(1);            // restore

Practical Traffic Patterns

Pattern 1 — Protocol traffic mix

SystemVerilog — realistic AXI traffic distribution
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class AxiTxn;
    rand bit        write;
    rand bit [2:0]  burst_type;  // 0=FIXED, 1=INCR, 2=WRAP
    rand bit [3:0]  burst_len;   // 0=1beat, 15=16beats
 
    // 60% reads, 40% writes
    constraint rw {
        write dist { 0 := 6, 1 := 4 };
    }
 
    // INCR dominates (most real traffic), FIXED and WRAP as minorities
    constraint burst_t {
        burst_type dist { 0 := 1, 1 := 8, 2 := 1 };
    }
 
    // Short bursts most common, medium occasional, long rare
    constraint burst_l {
        burst_len dist {
            [0:3]   :/ 70,   // 1–4 beats: 70% of traffic
            [4:7]   :/ 20,   // 5–8 beats: 20%
            [8:15]  :/ 10    // 9–16 beats: 10%
        };
    }
endclass

Pattern 2 — Corner-case emphasis

SystemVerilog — corner-case emphasis with dist
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class FifoDepth;
    rand bit [5:0] depth;   // 0–63
 
    // Hit boundary conditions often, middle occasionally
    constraint boundary_focus {
        depth dist {
            0           := 25,   // empty: 25%
            63          := 25,   // full:  25%
            [1:62]      :/ 50    // rest:  50% split across 62 values
        };
    }
endclass

dist vs soft — Choosing the Right Tool

ScenarioUse
You want exactly 70% reads, 30% writes over many callsdist
You want reads to be the default unless a test forces writessoft write == 0
You want corner values hit often but not to the exclusion of othersdist with high weights on corners
You want a base class to have defaults a child can freely overridesoft
You want all values equally likely but only within a setinside (no dist needed)

Common Pitfalls

Confusing := and :/ on ranges

[0:99] := 1 gives 100 values each with weight 1 — total weight from this item alone is 100. [0:99] :/ 1 gives the entire range a total weight of 1. The difference is enormous when other items have weight 1 too.

dist values outside other constraints

If a value in the dist list is excluded by another hard constraint, the solver fails. Keep dist ranges aligned with all other active constraints.

Expecting exact percentages in small runs

Weights are proportional probabilities, not guarantees per call. Over 10 calls, a 70/30 split might produce 8/2 or 6/4. The ratio holds statistically over hundreds of calls.

Using dist when randc gives better coverage

If you just need all values hit before any repeats, use randc. Reserve dist for when you genuinely need unequal probabilities, not just "visit everything."

Quick Reference

SystemVerilog — dist cheat sheet
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Basic syntax ──────────────────────────────────────────────
x dist { val1 := w1, val2 := w2, ... };
 
// ── := on a range: weight per value ──────────────────────────
x dist { [0:3] := 10 };    // 0,1,2,3 each get weight 10 → total 40
 
// ── :/ on a range: weight for the whole range ─────────────────
x dist { [0:3] :/ 10 };    // 0,1,2,3 share weight 10 → each gets 2.5
 
// ── Mixed — values + ranges ───────────────────────────────────
x dist { 0 := 20, [1:10] :/ 50, 11 := 30 };
 
// ── Weight 0 = exclude value ──────────────────────────────────
x dist { 0 := 9, 1 := 0, 2 := 1 };  // x=1 never generated
 
// ── Probability formula ───────────────────────────────────────
// P(item) = item_weight / sum_of_all_weights
// Weights are relative — only ratios matter
 
// ── dist inside a with clause ─────────────────────────────────
assert(obj.randomize() with { x dist { 0 := 7, 1 := 3 }; });

Verification Usage — Where dist Earns Its Keep in Real CRV

Across production UVM environments, weighted distributions cluster around four reliable patterns. Each addresses a coverage-driven need that pure uniform randomisation handles poorly.

Corner-case biasing on payload fields

The canonical use: skew payload values toward boundary corners (zero, all-ones, alternating bit patterns) that often expose RTL bugs but are statistically rare in uniform random.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class apb_xact;
  rand bit [31:0] pwdata;
  constraint c_data_dist {
    pwdata dist {
      32'h0000_0000 :=  5,   // 5% — boundary low
      32'hFFFF_FFFF :=  5,   // 5% — boundary high
      32'hAAAA_AAAA :=  3,   // 3% — alternating pattern
      32'h5555_5555 :=  3,   // 3% — alternating pattern (inverse)
      [32'h1 : 32'hFFFF_FFFE] := 84    // 84% — fills the rest
    };
  }
endclass</code>

Read/write/atomic transaction mix

Real workloads aren't 50/50 reads/writes. Cache-heavy benchmarks are 80% reads; streaming writes are 90% writes; coherency tests emphasise atomics. The dist shapes the protocol-level mix per test scenario.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>typedef enum bit [1:0] { READ, WRITE, ATOMIC_RMW, BARRIER } kind_e;
 
class apb_xact;
  rand kind_e kind;
  constraint c_kind_mix {
    kind dist {
      READ       := 65,
      WRITE      := 30,
      ATOMIC_RMW :=  4,
      BARRIER    :=  1
    };
  }
endclass</code>

Inter-transaction delay shaping

The driver's idle gap between transactions affects bus utilisation and exposes timing-related bugs. Realistic test mixes emphasise both back-to-back (gap=0) and long-idle (gap=large) patterns over pure uniform.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class apb_driver_seq;
  rand int unsigned idle_gap;
  constraint c_idle {
    idle_gap dist {
      0           := 40,   // 40% back-to-back
      [1:5]       := 30,   // 30% short gap
      [6:100]     := 25,   // 25% medium gap
      [101:1000]  :=  5    // 5% long gap
    };
  }
endclass</code>

Burst-length distribution for AXI/AHB/PCIe

Protocols allow many burst lengths but typical traffic clusters at small power-of-2 sizes. dist shapes the burst-length distribution to match realistic traffic without losing the ability to occasionally exercise edge cases.

Simulation Behavior — How the Solver Samples From a dist

Two-stage solve: feasibility then weighted sampling

The solver first computes the satisfying set (all values that meet every active constraint). It then applies the dist weights to that satisfying set: items present in both the satisfying set and the dist list keep their relative weights; items in the dist list excluded by other constraints have weight 0; values in the satisfying set not mentioned in the dist list get weight 0 too (unless an explicit "else" range covers them).

Weight semantics

Weights are relative, not percentages. The probability of any item is item_weight / sum_of_all_weights. { 0 := 1, 1 := 1 } and { 0 := 50, 1 := 50 } produce identical distributions. The convention of using percentages (weights summing to 100) is purely a readability tool, not a solver requirement.

:= vs :/ semantics

x := w assigns weight w to every value in x. x :/ w assigns weight w to x as a whole, with each value getting w / N (where N is the number of values in x). For single values, both are identical. For ranges, the operators are dramatically different — [10:19] := 5 gives the range weight 50 (each of 10 values gets 5); [10:19] :/ 5 gives the range weight 5 (each value gets 0.5).

Cost of dist-driven sampling

Distribution sampling adds a small per-call cost over uniform random: the solver computes the cumulative weight distribution, picks a uniform random number in [0, total_weight), and finds the corresponding item. Cost is O(number of dist items), typically negligible against the solve itself. For very large dist lists with implicit "else" ranges, the cost is dominated by the satisfying-set computation, not the sampling.

✅ Explicit operator, intended distribution
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [7:0] kind;
  // Equal probability for individual values vs ranges
  constraint c {
    kind dist {
      0        := 25,   // 25%
      255      := 25,   // 25%
      [1:254]  := 50    // 50% spread over middle
                         // (each middle value: 50/254 ≈ 0.2%)
    };
  }
endclass</code>
❌ Wrong operator — distribution off
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [7:0] kind;
  constraint c {
    kind dist {
      0        := 25,
      255      := 25,
      // Wanted "50% in middle" — used := so each
      // middle value gets weight 25, ratio:
      // total = 25 + 25 + 254*25 = 6400
      // 0 gets 25/6400 = 0.4% (not 25%!)
      [1:254]  := 25
    };
  }
endclass</code>

Waveform Analysis — Verifying the Distribution Matches Intent

A dist describes the long-run frequency of values. Verifying it produces the intended distribution requires statistical observation, not single-transaction inspection. The discipline: pair every dist with a coverpoint that bins the field by intended weight category.

The coverage-driven verification pattern

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class apb_xact;
  rand bit [31:0] pwdata;
  constraint c_data {
    pwdata dist {
      32'h0          :=  5,
      [32'h1 : 32'hFFFE_FFFE] := 90,
      32'hFFFF_FFFF  :=  5
    };
  }
  covergroup cg_dist;
    cp_data : coverpoint pwdata {
      bins zero      = {0};
      bins middle    = {[1 : 32'hFFFE_FFFE]};
      bins all_ones  = {32'hFFFF_FFFF};
    }
  endgroup
endclass</code>

ASCII view — coverage report validating the dist

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>After 10,000 randomize() calls:
 
    Coverpoint           Hits      Expected   Status
    -----------------------------------------------------
    cp_data.zero          487       500       ✓ (~5%)
    cp_data.middle       8941      9000       ✓ (~90%)
    cp_data.all_ones      572       500       ✓ (~5%)
    -----------------------------------------------------
    Total:              10000     10000       Dist OK</code>

Diagnosing "the distribution isn't what I expected"

If the coverage shows the zero-bin at 1% instead of the intended 5%, three likely causes: (a) the :=/:/ operator was wrong, scaling the weights incorrectly; (b) another constraint is excluding part of the dist list, re-normalising weights toward other items; (c) the iteration count is too low to converge statistically — at 100 calls, even a correct dist can show 1% on a 5% bin from pure randomness. Distinguish by running with 10x more iterations.

Industry Insights — Hard-Won Lessons About Weighted Distributions

Debugging Academy — Five Real Bugs From dist Misuse

1

"Boundary case appears 0.4% of the time, not 25%"

DEBUG
Symptom

Coverage after 10,000 transactions shows the all-ones boundary at 0.4% hits, not the intended 25%.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [7:0] pwdata;
  constraint c {
    pwdata dist {
      0          := 25,   // 25% zero
      [1:254]    := 25,   // BUG: meant "25% middle" but := gives weight to each
      255        := 25    // 25% all-ones
    };
  }
endclass</code>
Root Cause

:= assigns the weight to each value in a range. [1:254] := 25 gives 254 items × 25 = 6350 total weight to the middle, vs 25 for zero and 25 for all-ones. Ratios: zero = 25/6400 ≈ 0.4%, middle ≈ 99.2%, all-ones ≈ 0.4%. Not what was intended.

Fix

Use :/ for the range: pwdata dist { 0 := 25, [1:254] :/ 50, 255 := 25 };. The range gets weight 50 shared across all values (each gets 50/254 ≈ 0.2). Total = 25+50+25 = 100. Now zero = 25%, middle = 50%, all-ones = 25%.

2

"dist appears to do nothing — distribution is uniform"

DEBUG
Symptom

A dist declared with weights 50/50 between two values produces ~50/50, but a 90/10 dist between the same two values still produces ~50/50.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [3:0] kind;
  constraint c_dist { kind dist { 0 := 90, 1 := 10 }; }
  constraint c_kind { kind inside {0, 1}; }
endclass
 
// Some pre-existing constraint:
// constraint c_kind_force { kind != 0; }   // forces non-zero
//                                            (added elsewhere, forgotten)
 
// Result: 0% zero, 100% kind=1, dist appears ignored</code>
Root Cause

A hard constraint elsewhere excludes kind = 0. The solver computes the satisfying set first (which is {1}), then applies the dist — but only value 1 is in both the dist list and the satisfying set, so probability collapses to 100% one. The dist weights become irrelevant when the hard constraint zeros out one of them.

Fix

Find and either remove or relax the conflicting hard constraint. If the intent is "usually zero, sometimes one," the conflicting constraint defeats the purpose entirely. Distribution debugging always starts with "what's the satisfying set?" — print it explicitly and verify it includes every value the dist mentions.

3

"foreach with dist takes forever to solve"

DEBUG
Symptom

A constraint with foreach (data[i]) data[i] dist { ... } on a 32-element array makes randomize take 200ms per call; a regression that should run in 5 minutes takes 4 hours.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class burst_xact;
  rand bit [31:0] data[];
  constraint c_size { data.size() == 32; }
  constraint c_data {
    foreach (data[i])
      data[i] dist { 0 := 5, [1:'hFFFE] := 90, 'hFFFF_FFFF := 5 };
  }
endclass</code>
Root Cause

The solver computes the joint distribution over 32 array elements simultaneously. The cost grows roughly as 3^32 across possible boundary-mix configurations — astronomical for the solver to enumerate exactly.

Fix

Move per-element randomisation to post_randomize: function void post_randomize(); foreach (data[i]) begin int w = $urandom_range(0, 99); data[i] = (w < 5) ? 0 : (w < 95) ? $urandom_range(1, 'hFFFE) : 'hFFFF_FFFF; end endfunction. Same statistical distribution; the per-element work is O(1) instead of joint-solved across 32 fields.

4

"Two dist constraints on the same field — only one takes effect"

DEBUG
Symptom

A class has two constraint blocks each containing a different dist on the same field. The resulting distribution matches one of them but not the other.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [7:0] kind;
  constraint c1 { kind dist { 0 := 60, 1 := 40 }; }
  constraint c2 { kind dist { 0 := 30, 1 := 70 }; }
endclass
 
// What's the actual distribution? Neither 60/40 nor 30/70.
// It's the intersection of both, weights multiplied/normalised
// — usually implementation-defined.</code>
Root Cause

Two dist clauses on the same field don't combine into a defined joint distribution — the LRM allows implementations to pick one, multiply weights, or anything in between. The result is non-portable across simulators.

Fix

Never have two dist constraints on the same field. Consolidate into one block with the intended distribution. If the two were meant to model different test scenarios, use constraint_mode to switch between them, or build a single combined dist whose weights reflect both intents.

5

"dist with implicit else range omitted — values outside the dist appear with no weight"

DEBUG
Symptom

A test expected zero, all-ones, and 0xCAFE_BABE to each appear ~33% of the time. Actual: zero appears, all-ones appears, but values between them appear at random — apparently with no respect to the dist.

Buggy Code
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<code>class xact;
  rand bit [31:0] pwdata;
  // BUG: dist lists three values; everything else has weight 0 in dist —
  // but the field still has those values in its type range
  constraint c {
    pwdata dist {
      0           := 1,
      'hCAFE_BABE := 1,
      'hFFFF_FFFF := 1
    };
  }
endclass</code>
Root Cause

Actually this is the correct interpretation: dist lists exactly three values, so the satisfying set is restricted to those three values, each with probability 1/3. If the user observes values between them, the source is elsewhere — usually another randomize path that doesn't include this constraint, or a constraint disabled via constraint_mode(0).

Fix

Verify by printing the satisfying set explicitly. If the constraint is active, pwdata can only take the three values listed. If pwdata takes other values, either: (a) the constraint is disabled (check constraint_mode()), (b) there's a parent class constraint widening it, or (c) some other code path is overriding. The dist itself is working correctly.

Interview Q&A — Twelve Questions You Will Be Asked

It declares a weighted probability distribution over the values a rand/randc field can take. The solver finds a satisfying assignment respecting all hard constraints, then samples from that satisfying set with the given weights. x dist { 0 := 50, [1:9] := 50 } means "50% of the time pick 0, 50% pick uniformly in [1:9]."

Best Practices — Ten Rules for dist Discipline

  1. Use weights that sum to 100 for readability. Each weight then reads directly as a percentage; no mental arithmetic required.
  2. Choose := for "each value in this range gets this weight." Choose :/ for "this entire range shares this weight." Verify the choice matches intent at code-review time.
  3. Pair every dist with a coverpoint that mirrors its weight categories. The coverage confirms the dist is producing the intended frequencies.
  4. Comment every weight with its rationale. Measured workload? Coverage convergence target? Spec requirement? Future maintainers need the answer in the source.
  5. Don't put two dist constraints on the same field. Behaviour is implementation-defined and non-portable. Consolidate.
  6. Avoid foreach with dist on large arrays. Joint distribution solving is exponentially expensive. Use procedural per-element randomisation in post_randomize instead.
  7. Choose between dist, soft, and hard constraints by intent. Dist for probabilistic shaping; soft for overridable defaults; hard for required rules. Mixing the wrong tool produces silent surprise.
  8. Verify dist behaviour during class onboarding. A 10,000-call sanity test with coverage validation catches operator and weight bugs before they distort months of regression data.
  9. Encode real-workload statistics into dist weights. Uniform random verifies a fantasy workload; weighted random verifies the workload silicon will actually see.
  10. Remember dist samples from the satisfying set, not the type range. When other constraints exclude values, the dist re-normalises silently — diagnose distribution surprises by printing the satisfying set first.