An exclusion removes a bin, cell, or hierarchy from the closure goal. It is the correct tool exactly when the scenario cannot or need not occur, and a falsification of the metric in every other case.
The legitimacy test is simple: would hitting this bin teach anything the plan wants to know? If yes, it stays.
Structurally unreachable: tied-off inputs, hardware configs absent from this product, dead modes confirmed by the designer.
Spec-forbidden combinations that are separately checked: the illegal_bins or assertion supplies the evidence, and the cross cell leaves the goal.
Out-of-scope hierarchy: vendor IP verified by its supplier, behavioral models, test harness internals.
Deferred features: explicitly not in this release, with the plan row marked deferred and revived next release.
Auto-product residue: cross cells the tool generated that no plan row ever demanded. Better to prune these in the model with ignore_bins.
The listing below is not run in this course's checks: the open simulators do not count covergroups, so the lesson's bench counts it with the bins model.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
interface mode_cov_if(input logic clk);
logic single_ch; // 1 in the single-channel SKU
logic [3:0] op; // 0-7 single-op, 8-15 dual-op
covergroup mode_cg @(posedge clk);
cp_cfg : coverpoint single_ch {
bins single_ch = {1};
bins dual_ch = {0};
}
cp_op : coverpoint op {
bins single_op = {[0:7]};
bins dual_op = {[8:15]};
}
x_mode : cross cp_cfg, cp_op {
// EXCL-014: dual-channel ops are impossible in the single-channel SKU.
// Approved: verification lead plus designer. Spec s2.1 table 2-1.
ignore_bins single_sku = binsof(cp_cfg.single_ch) &&
binsof(cp_op.dual_op);
}
endgroup
endinterfaceA waiver is the audit record behind an exclusion. The rule of thumb: a waiver must let a stranger, a year later, decide whether the exclusion is still valid, without finding the people involved.
Field | Example |
|---|---|
id | EXCL-014: stable, referenced from code |
target |
|
reason | The single-channel SKU has no dual-op datapath |
evidence | Spec s2.1 table 2-1; designer confirmation (link) |
class | Structurally unreachable |
approved | Verification lead and designer, 2026-04-02 |
expires | Re-review at the next SKU addition or major spec revision |
Who approved, why, and a link to the spec are the non-negotiable three. An exclusion nobody can defend a year later is a hole wearing a costume.
Version-control the exclusion file with the testbench. It is part of the coverage model, and its diff history is audit evidence.
One exclusion per entry, each with its waiver ID. No wildcard patterns that silently swallow future bins.
Re-validate on RTL or spec change. Yesterday's unreachable becomes reachable when a tie-off is removed.
Track the exclusion count as a metric. A rising curve near a deadline is itself a review finding.
Prefer ignore_bins in the model over tool-side exclusion files where possible. Code review sees the former by default.
Every verification organization has the story. Friday before sign-off, coverage is at 93%, and a bulk exclusion of miscellaneous unreachable crosses lands the report at 100%.
Months later a silicon bug traces to a cell inside that bulk exclusion. It was reachable all along, just never analyzed.
Honest closure | Bulk exclusion at deadline | |
|---|---|---|
The last 7% | Triaged: 4% steered and closed, 2% directed and closed, 1% waived, each documented | Excluded as a block: no per-cell analysis, no designer confirmation, one commit of 400 cells |
Result | Every zero-hit cell has a name, an owner and a reason | An unknown number of reachable, untested scenarios ship |
Later | The silicon-bug post-mortem finds the exclusion commit |
Exclude as out-of-scope hierarchy, with a waiver naming the supplier evidence. Do not spend closure effort on logic owned elsewhere.
Walk every added entry item by item. A deadline spike in exclusions is a finding, and bulk entries hide reachable scenarios.
The bench builds the x_mode cross twice with the bins model: once with the documented EXCL-014 exclusion only, and once with a deadline commit that excludes every cell still at zero. The regression never runs a dual-op on the dual-channel SKU; months later, a test does.
Before: 2 of 3 with EXCL-014, naming <dual_ch, dual_op> as the hole; 2 of 2, 100%, with the bulk exclusion. After: the honest cross closes at 3 of 3, while the bulk cross still reads 100% and silently discards the hit, because the reachable cell was excluded.
// uses: covmodel
// One documented exclusion against a deadline bulk exclusion, on the same regression.
module tb_waivers;
cm_point cp_cfg = new("cp_cfg"), cp_op = new("cp_op");
cm_cross x_honest, x_bulk;
function void observe(int single_ch, int op);
cp_cfg.sample(single_ch); cp_op.sample(op); x_honest.sample(); x_bulk.sample();
endfunction
initial begin
cp_cfg.add("single_ch", 1, 1); cp_cfg.add("dual_ch", 0, 0);
cp_op.add("single_op", 0, 7); cp_op.add("dual_op", 8, 15);
x_honest = new("x_mode (EXCL-014 only)", cp_cfg, cp_op);
x_bulk = new("x_mode (bulk exclusion)", cp_cfg, cp_op);
// EXCL-014: dual-channel ops are impossible in the single-channel SKU. Spec s2.1 table 2-1.
x_honest.ignore(x_honest.sel("single_ch", "dual_op"));
// The regression before the deadline: no test ever ran a dual-op on the dual-channel SKU.
for (int op = 0; op < 8; op++) begin observe(1, op); observe(0, op); end
// Friday: "exclude every cell still at zero", in one commit.
foreach (cp_cfg.bin_list[i]) foreach (cp_op.bin_list[j])
if (x_bulk.hits[x_bulk.idx(i, j)] == 0) x_bulk.ignore(x_bulk.sel(cp_cfg.bin_list[i].name, cp_op.bin_list[j].name));
x_honest.report(); x_bulk.report();
// Months later a test finally runs a dual-op on the dual-channel SKU, the reachable cell.
observe(0, 12);
$display("after a dual-op on the dual-channel SKU:");
x_honest.report(); x_bulk.report();
if (x_honest.n_bins() != 3 || x_honest.get_coverage() != 100.0 || x_bulk.n_bins() != 2 || x_bulk.hits[x_bulk.idx(1, 1)] != 0)
$fatal(1, "FAIL: waiver math");
$display("PASS: the documented exclusion named the reachable hole; the bulk exclusion read 100%% and swallowed it");
$finish;
end
endmodule cross x_mode (EXCL-014 only): 66.67% (2 of 3 bins)
hole: <dual_ch, dual_op>
cross x_mode (bulk exclusion): 100.00% (2 of 2 bins)
after a dual-op on the dual-channel SKU:
cross x_mode (EXCL-014 only): 100.00% (3 of 3 bins)
cross x_mode (bulk exclusion): 100.00% (2 of 2 bins)
PASS: the documented exclusion named the reachable hole; the bulk exclusion read 100% and swallowed itCounted with the course's bins model (rich/coverage/cov_model.sv, listed on the Coverage hub) on Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator 5.048 parses covergroups with a COVERIGN warning and ignores their bins, and Icarus 13 has none, so each covergroup listing is marked not run and its bench counts the same bins with the model.
With EXCL-014 alone the cross named the reachable hole; the bulk exclusion read 100% and silently discarded the later hit.