Part 5 · Functional Coverage · Intermediate
Pruning Crosses: ignore_bins & illegal_bins
Removing impossible combinations from the goal math, illegal cross combinations as runtime checks, and the percentage effect of pruning.
Why crosses need pruning
The full product almost always contains combinations the design or the environment makes impossible. A read transaction never carries error injection; a single-beat burst has no wrap mode. Left in the cross, those cells sit at zero hits forever and cap your achievable percentage below 100% . ignore_bins removes them from the denominator. illegal_bins goes further: it removes them from the goal and fires a runtime error if a sample ever lands there — turning the cross into a checker.
covergroup err_cg @(posedge clk iff txn_done);
cp_dir : coverpoint is_write { bins rd = {0}; bins wr = {1}; }
cp_err : coverpoint err_mode { bins none = {2'b00};
bins parity = {2'b01};
bins crc = {2'b10}; }
x_dir_err : cross cp_dir, cp_err {
// Environment fact: error injection only exists on the write path.
// read x parity and read x crc are IMPOSSIBLE -> remove from goal.
ignore_bins rd_no_inject =
binsof(cp_dir.rd) && (binsof(cp_err.parity) || binsof(cp_err.crc));
}
endgroupThe matrix before and after pruning
BEFORE AFTER ignore_bins
none parity crc none parity crc
┌─────┬───────┬─────┐ ┌─────┬───────┬─────┐
rd │ ✓ │ ✗ │ ✗ │ rd │ ✓ │ (gone)│(gone)│
├─────┼───────┼─────┤ ├─────┼───────┼─────┤
wr │ ✓ │ ✓ │ ✓ │ wr │ ✓ │ ✓ │ ✓ │
└─────┴───────┴─────┘ └─────┴───────┴─────┘
goal = 6 cells, 4 hittable goal = 4 cells, 4 hittable
max achievable = 4/6 = 66.7% max achievable = 4/4 = 100%The percentage math, worked
Cross coverage percentage is hit cells divided by goal cells. Pruning changes the denominator, so the same simulation results read very differently before and after.
WORKED EXAMPLE — same regression, same hits
Full cross: 2 dir bins × 3 err bins = 6 cells
Truly possible: 4 cells (rd×parity, rd×crc unreachable)
Regression hit: 4 cells (every reachable one)
Without pruning: 4 hit / 6 goal = 66.7% ← looks like a hole problem
With ignore_bins: 4 hit / 4 goal = 100.0% ← honest closure
The 33.3% gap was never a stimulus gap. It was a modeling bug:
the coverage model demanded combinations the design cannot produce.This is why hole triage starts by asking 'is this cell reachable?' before asking 'which test hits it?'. An unreachable cell belongs in ignore_bins with a comment citing the spec line that forbids it — not in a directed-test backlog.
illegal_bins — a cross cell as a protocol check
Use illegal_bins when a combination is not merely outside the plan but forbidden by the spec: if it occurs, something is broken — in the DUT or in the testbench. The simulator raises a runtime error at the sample that lands in the cell, with the failing combination in the message.
x_mode_size : cross cp_mode, cp_size {
// Spec 4.3.1: WRAP bursts must be 2, 4, 8 or 16 beats.
// WRAP x size-1 is a protocol violation, not a coverage hole.
illegal_bins wrap1 = binsof(cp_mode.wrap) && binsof(cp_size.b1);
// Environment limitation (not a spec violation): exclude silently.
ignore_bins no_fixed_long =
binsof(cp_mode.fixed) && binsof(cp_size.b16);
}ignore vs illegal — the decision rule
ignore_bins — combination cannot or need not happen; remove from goal, no error if it somehow occurs in most tools' default mode.
illegal_bins — combination must never happen; removed from goal AND a runtime error fires on a hit.
If you would file a bug when the combination occurs, it is illegal_bins. If you would shrug, it is ignore_bins.
Both need a comment with the spec or environment reason — uncommented pruning is the first thing a coverage review flags.
Interview angle: "Your cross is stuck at 75% — what do you do?" Strong answers triage reachability first: compute which cells are zero, check each against the spec, move the impossible ones to ignore_bins (showing the percentage math), make the forbidden ones illegal_bins, and only then write stimulus for the rest. Jumping straight to 'write more tests' is the junior tell.
Key takeaways
ignore_bins shrinks the goal denominator — unreachable cells stop capping your percentage.
illegal_bins removes the cell from the goal and converts it into a runtime protocol check.
Decision rule: would a hit be a bug? illegal_bins. Just irrelevant or impossible? ignore_bins.
Every pruned cell needs a documented reason — pruning without justification is silent goal-rigging.
Common pitfalls
Leaving impossible combinations in the cross — the percentage plateaus and the team chases phantom holes.
Using ignore_bins on combinations that are merely hard to hit — that is hiding a stimulus gap, not pruning.
Using illegal_bins for environment limitations — a legal DUT behavior then kills the simulation.
Pruning at deadline to push 92% to 100% — reviewers diff the exclusion list; bulk late pruning is a red flag.