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.
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.
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.
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]}; }
endgroupFIXED 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 NChoosing 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.