Part 5 · Functional Coverage · Intermediate

Auto Bins vs Explicit Bins

Auto-bin semantics, the auto_bin_max default-64 silent bucketing trap, explicit value and range bins, and bins b[] vs bins b[4].

What auto bins actually do

A coverpoint with no bin declarations gets automatic bins: ideally one bin per possible value of the expression. But the LRM caps the count at option.auto_bin_max, whose default is 64 . When the value space exceeds the cap, the simulator silently divides the range into 64 equal-width buckets. No warning. An 8-bit coverpoint quietly becomes 64 bins of 4 adjacent values each — hitting any one of the four marks the bucket covered.

diagram
THE SILENT BUCKETING TRAP

  coverpoint len;          // len is bit [7:0], no explicit bins

  what you think you get        what you actually get
  ──────────────────────        ─────────────────────
  256 bins, one per value       64 bins, 4 values each
                                auto[0]   = {0,1,2,3}
                                auto[1]   = {4,5,6,7}
                                ...
                                auto[63]  = {252,253,254,255}

  consequence: len==0 and len==3 are THE SAME BIN.
  Your "we covered length zero" claim is unverifiable —
  a hit on auto[0] may have been len==2 every time.

  Wider fields are worse: bit [31:0]  64 buckets of ~67M values.

Auto bins are legitimate for narrow fields: a 4-bit opcode gets 16 per-value bins, an enum gets one named bin per literal (a special rule that ignores auto_bin_max). Beyond 6 bits, auto bins stop meaning what you think they mean.


Explicit value and range bins

Explicit bins replace the value-space carve-up with named, plan-driven buckets. A bin can hold a single value, a list, ranges, or any mix — and its name is what appears in the report.

systemverilog
covergroup cg with function sample(bit [7:0] len);
  cp_len : coverpoint len {
    bins zero      = {0};                 // single value
    bins one       = {1};
    bins small     = {[2:8]};             // one bin for the range
    bins medium    = {[9:64]};
    bins large     = {[65:254]};
    bins max_len   = {255};               // boundary as its own bin
    bins odd_picks = {3, 17, 129};        // list → still ONE bin
  }
endgroup
// 7 goal bins. Hitting ANY value in [2:8] covers "small".
// The report reads like the verification plan.

Arrays of bins: bins b[] vs bins b[4]

Sometimes the plan really does require each value (or each chunk) separately. Appending [] to the bin name turns one bin into an array: one element per value in the set. Appending a fixed size [N] creates exactly N bins, distributing the values across them.

systemverilog
covergroup cg with function sample(bit [7:0] len);
  // ONE bin — any value in 0..15 covers it
  cp_a : coverpoint len { bins any_low      = {[0:15]}; }

  // SIXTEEN bins — every value 0..15 must be hit individually
  cp_b : coverpoint len { bins each_low[]   = {[0:15]}; }

  // FOUR bins — 16 values distributed 4 per bin:
  // each_q[0]={0..3} each_q[1]={4..7} each_q[2]={8..11} each_q[3]={12..15}
  cp_c : coverpoint len { bins each_q[4]    = {[0:15]}; }
endgroup
diagram
FIXED vs DYNAMIC BIN COUNTS

  bins b   = {[0:15]};   1 bin    "the range was touched"
  bins b[] = {[0:15]};   16 bins  "every value was seen"
  bins b[4]= {[0:15]};   4 bins   "every quarter was seen"

  choose by asking: what must I PROVE happened?
  ├─ range entered at all         single range bin
  ├─ every value matters          b[]   (mind explosion!)
  └─ resolution between extremes  b[N] with deliberate N

Choosing between them

  • Single range bin — when entering the region at all is the scenario (e.g. “a large burst happened”).

  • bins b[] — when each value is a distinct plan item (opcodes, channel IDs, small mode fields).

  • bins b[N] — when you need resolution across a wide range without per-value explosion.

  • bins b[] on a wide range is auto-bin explosion you wrote yourself — same trap, your own fingerprints.

  • Interview angle: “difference between bins b = {[0:15]} and bins b[] = {[0:15]}?” is a standard screen — answer with goal-bin counts (1 vs 16).

Key takeaways

  • Auto bins cap at auto_bin_max (default 64) and silently range-bucket anything wider than 6 bits.

  • Explicit bins are named, plan-driven buckets — one bin can hold values, lists, and ranges.

  • bins b[] explodes a set into per-value bins; bins b[N] distributes it across exactly N bins.

  • The bin count IS the coverage goal — every bin you create is a scenario you promise to hit.

Common pitfalls

  • Trusting auto bins on an 8-bit-plus field — boundary values vanish into 4-value buckets with no warning.

  • Fixing the trap by raising auto_bin_max — now you have 256 anonymous bins instead of intent.

  • bins b[] over a wide range — self-inflicted bin explosion that makes closure impossible.

  • Forgetting that a value list {3,17,129} in one bin is satisfied by ANY of them — use [] if each must occur.