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.

systemverilog
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));
  }
endgroup

The matrix before and after pruning

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

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

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