Functional coverage proves planned scenarios happened. It cannot prove the plan was complete, that checks were watching, or that the design is stabilizing.
Sign-off therefore reads four instruments together, each covering the others' blind spots.
Instrument | Proves | Blind spot it covers |
|---|---|---|
Functional coverage, merged, against the plan | Planned scenarios occurred | None: this is the plan's own view |
Code coverage: line, branch, toggle, FSM | RTL structure executed, including logic no plan row mentions | Scenarios the plan missed: unhit code means unplanned logic |
Assertion status: pass counts and cover properties | Checks were active and fired on real traffic | Passing by silence: an assertion never triggered checked nothing |
Bug curve: bugs per week | The discovery rate is decaying | High coverage with a rising bug curve says the plan is too small |
Sign-off is all four healthy, reviewed together, per feature. The code-coverage blind spot runs the other way too: Building Your First Coverage Model runs a FIFO test with Verilator line coverage, where every line and both sides of every branch ran while the corner bin that mattered read 0.
Functional 100% plus a code-coverage hole means logic exists that no plan row exercises. That is a plan gap, so investigate before waiving.
Code 100% plus functional 60% means lines toggled but scenarios untried. That is code coverage's classic false comfort.
An assertion with zero activations on merged runs never watched anything. Its pass is vacuous.
A flat bug curve plus open coverage ends the argument about the curve. The remaining holes are exactly where untested bugs live.
Aggregate percentages average across the model, and averages hide zeros. Sign-off must read coverage per plan feature, because no aggregate weights features by importance.
Feature | Bins | Hit | Feature % |
|---|---|---|---|
Burst shapes | 420 | 420 | 100% |
Address map | 310 | 310 | 100% |
QoS arbitration | 250 | 250 | 100% |
Error recovery | 20 | 0 | 0%: the one that escapes to silicon |
Bin-count total (tool summary view) | 1000 | 980 | 98% |
LRM rollup, mean of the 4 features | 75% |
98% sounds done, and 75% reads like ordinary slow progress. Neither number names the missing feature. Error recovery, 20 bins out of 1000, moves the bin-count total only 2 points, yet it is the feature most likely to be hit in the field and least likely to be hit by random stimulus.
The sign-off rule: report the minimum feature percentage, not just the aggregate. A feature at 0% is a veto regardless of the average.
This is also where option.weight earns its keep: weighting plan-critical groups higher makes the aggregate harder to game. The per-feature table, not the weighted average, is still what the review reads.
Merged functional coverage meets goal per feature: minimum feature percentage on the table, not just the aggregate.
Every zero-hit bin is closed, or carries a waiver with approver, reason, and spec link.
Code coverage meets target, and unhit code is explained or traced to a new plan row.
Every assertion activated at least once in the merged regression, with cover properties hit for key sequences.
Bug discovery curve flat for the agreed window, commonly two weeks or more of full regression.
Exclusion-file diff since last review walked item by item. Late additions get the most scrutiny.
Plan-to-spec delta reviewed: spec changes since the plan was written have corresponding plan rows.
Regression health confirmed: pass rate, no masked failures, seeds and test list archived with the merged database.
No. Report the minimum feature number, not the average, and treat a 0% feature as a veto regardless of the aggregate.
No. A check that never watched traffic checked nothing. Activation counts are part of the evidence.
The bench builds the four features from the table with the bins model, hits every bin of the first three and none of error recovery, and reads the three numbers a sign-off report could show.
98% by bin count (980 of 1000), 75% by rollup (the mean of 100, 100, 100 and 0), and 0% for the minimum. Only the minimum names error recovery and raises the veto.
// uses: covmodel
// Four plan features: the bin-count total, the LRM rollup and the minimum feature, from the same hits.
module tb_signoff;
cm_group dma = new("dma coverage model");
cm_point feat[4];
initial begin
string names[4] = '{"burst shapes", "address map", "qos arbitration", "error recovery"};
int nbins[4] = '{420, 310, 250, 20};
int total = 0, hit = 0;
real lowest = 100.0;
foreach (feat[f]) begin
feat[f] = new(names[f]);
feat[f].add_each("b", 0, nbins[f] - 1);
if (f != 3) for (int v = 0; v < nbins[f]; v++) feat[f].sample(v); // error recovery never ran
dma.points.push_back(feat[f]);
total += feat[f].n_bins(); hit += feat[f].n_covered();
if (feat[f].get_coverage() < lowest) lowest = feat[f].get_coverage();
$display(" feature %-16s %4d bins %4d hit %6.2f%%", names[f], feat[f].n_bins(), feat[f].n_covered(), feat[f].get_coverage());
end
$display(" bin-count total %0d of %0d = %0.2f%%", hit, total, 100.0 * hit / total);
$display(" LRM rollup %0.2f%%", dma.get_coverage());
$display(" minimum feature %0.2f%%%s", lowest, lowest == 0.0 ? " <- veto" : "");
if (100.0 * hit / total != 98.0 || dma.get_coverage() != 75.0 || lowest != 0.0) $fatal(1, "FAIL: sign-off math");
$display("PASS: 98%% by bin count and 75%% by rollup, and only the minimum names the 0%% feature");
$finish;
end
endmodule feature burst shapes 420 bins 420 hit 100.00%
feature address map 310 bins 310 hit 100.00%
feature qos arbitration 250 bins 250 hit 100.00%
feature error recovery 20 bins 0 hit 0.00%
bin-count total 980 of 1000 = 98.00%
LRM rollup 75.00%
minimum feature 0.00% <- veto
PASS: 98% by bin count and 75% by rollup, and only the minimum names the 0% featureCounted 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.
Four features read 98% by bin count and 75% by rollup; only the minimum, 0%, named error recovery.