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”.

systemverilog
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”.

diagram
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.

systemverilog
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.