Part 5 · Functional Coverage · Intermediate
Range, Array & Default Bins
Bins over ranges, the [N] split math for distributing ranges, default catch-all bins and why they don't count toward coverage, and $ in ranges.
Range bins and $
Range bins bucket contiguous value spans. The $ token stands for the minimum or maximum value of the coverpoint's type, letting you write open-ended ranges that survive width changes: {[1024:$]} means “1024 up to the type's maximum” and {[$:7]} means “minimum up to 7”.
covergroup cg with function sample(bit [15:0] addr);
cp_addr : coverpoint addr {
bins boot_rom = {[$:16'h00FF]}; // 0x0000-0x00FF
bins peripherals = {[16'h0100:16'h0FFF]};
bins sram = {[16'h1000:16'h7FFF]};
bins high_mem = {[16'h8000:$]}; // 0x8000-0xFFFF
}
endgroup
// Width change to bit [17:0]? $-bounded bins adapt; the
// hand-written hex constants in the middle do not. Review both.The [N] split math
When a range is distributed across a fixed-size bin array bins b[N] = {[lo:hi]}, the simulator deals the values into N bins. When the value count divides evenly, each bin gets count/N consecutive values. When it does not divide evenly, the LRM puts the excess in the last bin — so the final bin is larger. Knowing this math matters when you claim “each eighth of the address space was touched”.
RANGE-TO-ARRAY DISTRIBUTION MATH
bins q[4] = {[0:15]}; 16 values / 4 bins = 4 each
q[0]={0..3} q[1]={4..7} q[2]={8..11} q[3]={12..15}
bins t[3] = {[0:9]}; 10 values / 3 bins = 3 each, rem 1
t[0]={0..2} t[1]={3..5} t[2]={6..9} ◄── last bin gets 4
bins h[4] = {[0:255]}; 256 / 4 = 64 each
h[0]={0..63} h[1]={64..127} h[2]={128..191} h[3]={192..255}
multiple ranges in the set are concatenated first, then dealt:
bins m[2] = {[0:3],[8:11]}; → m[0]={0..3} m[1]={8..11}If N exceeds the number of values in the set, you get one bin per value and the extra bins are not created — effectively the same as b[]. Tools warn on this; treat the warning as a design smell.
default bins: bookkeeping, not coverage
A default bin catches every value not claimed by any other bin in the coverpoint. Its crucial property: default bins do not count toward coverage — they are excluded from the goal denominator and the numerator alike. A default bin is a tripwire for unexpected values, not a scenario. Use it to detect that something outside your bin plan occurred (hit count > 0 means your bins or your understanding are incomplete), never to vacuum up values you were too lazy to classify.
covergroup cg with function sample(bit [7:0] cmd);
cp_cmd : coverpoint cmd {
bins read = {8'h01};
bins write = {8'h02};
bins flush = {8'h10};
bins others = default; // tripwire: anything unlisted
}
endgroup
// Coverage = hit(read,write,flush) / 3. "others" is NOT in the math.
// In the report: others hit-count 47 → 47 samples carried command
// codes your bin plan never anticipated. Investigate.
// default sequence — same idea for transition bins:
// bins unexpected = default sequence;Putting the three together
Range bins for plan regions; name them after the spec's memory map or size classes.
$-bounded ranges at the extremes so width changes don't silently orphan values.
b[N] arrays when resolution across the range is itself the requirement — verify the split math.
One default bin per hand-binned coverpoint as a completeness tripwire — alert on nonzero hits in review.
Interview angle: “does a default bin count toward coverage?” — no, and explaining the denominator math is the differentiator.
Key takeaways
$ in a range means type-min or type-max — open-ended bins that survive width changes.
b[N] deals values across N bins; uneven counts put the remainder in the LAST bin.
default bins catch unclassified values but are excluded from coverage percentage entirely.
A nonzero default-bin hit count means your bin plan missed real behavior — review it like a failure.
Common pitfalls
Assuming default contributes to coverage — it never does; the percentage ignores it in both directions.
b[N] where N doesn't divide the range and the last-bin remainder breaks your “equal slices” claim.
Hard-coded hex range bounds that silently misalign after a bus-width change — use $ at the extremes.
Using default as a catch-all scenario bin — it hides exactly the unexpected values it was meant to expose.