Verification projects move through recognizable phases, each with its own exit criterion. Knowing the current phase tells the team what to measure: counting coverage during bring-up is noise, and counting written tests during closure is noise.
VERIFICATION TIMELINE
BRING-UP FEATURE-COMPLETE COVERAGE CLOSURE CONVERGENCE
─────────────────►─────────────────────►───────────────────►────────────►
first txn every plan row has merged coverage bug rate
through DUT, a test exercising hits goal, holes flat-lines
smoke passes it at least once analyzed/waived near zero
exit: smoke exit: 100% plan exit: coverage exit: N weeks
green on every rows “passing” goal + hole clean regression
checkin review done + bug review
bugs/week: ramps up peaks then falls tail
(TB bugs first, (random finds the (each late bug
then DUT bugs) weird ones) is a red flag)Bring-up: one transaction end to end, testbench bugs fixed, regression skeleton standing. Days to a couple of weeks.
Feature-complete: tests until every plan row is exercised. A climbing bug rate is success here, not failure.
Coverage closure: seeds plus directed fills until merged functional coverage hits goal, with every hole dispositioned in writing.
Convergence: regression soak. The exit is consecutive clean weeks at full volume, not a calendar date.
Four numbers, tracked weekly, tell the real story. The trend matters more than the value: flat coverage for three weeks alarms louder than a low number climbing fast.
WEEKLY METRICS DASHBOARD
Metric │ Week 6 │ Week 7 │ Week 8 │ Read it as...
──────────────────┼────────┼────────┼────────┼──────────────────────────
Regression pass % │ 91% │ 96% │ 94% │ health of TB + RTL now
Functional cov % │ 61% │ 72% │ 74% │ progress toward closure
(merged) │ │ │ │ slope flattening → holes?
Open bugs (P1/P2) │ 9/14 │ 6/16 │ 3/11 │ P1 trend gates milestones
Code churn │ high │ med │ med │ RTL still moving →
(RTL commits/wk) │ │ │ │ coverage results are stale
Plus per-phase extras:
- bring-up: tests passing smoke
- closure: uncovered bins by feature area, seeds/night
- convergence: weeks since last P1, regression runtimeThe next listing is a coverage module with no testbench, so this course's checks do not run it. It shows what one merged-coverage bin looks like.
// not-run: fragment: a coverage module with no testbench top
module fifo_fill_cov (
input logic clk,
input logic [2:0] fill
);
covergroup cg_fill @(posedge clk);
cp_fill: coverpoint fill {
bins empty = {0};
bins mid = {[1:3]};
bins full = {4};
}
endgroup
cg_fill cov = new();
endmoduleEvery dashboard number can be gamed, usually by accident. Read each metric adversarially: name the failure mode that still produces this value, then run the audit that rules it out.
METRIC vs REALITY
Dashboard says Could actually mean
──────────────────────── ─────────────────────────────────────
pass rate 100% → checkers too weak to fail
coverage 95% → trivial bins; real corners unmeasured
bug rate ~0 → stimulus exhausted, not design clean
churn low → designers stopped committing...
or stopped fixing
Defense: pair every metric with its audit
pass rate ↔ bug-injection spot checks
coverage ↔ bin-quality review in hole analysis
bug rate ↔ stimulus freshness (new seeds finding new bins?)A test with no scoreboard passes everything. Audit it by planting a bug and confirming a test catches it.
One bin per feature closes fast and proves little. Audit bins during hole review, not after.
Random stimulus with converged constraints stops exploring. Watch the new-seed coverage delta, not just totals.
Track the test count beside the pass percentage, so disabled tests cannot inflate the rate quietly.
Audit trigger. Either the design converged, the checkers are too weak to fail, or failing tests were disabled. The bug-injection spot check below tells which.
The flat 70%. Trends beat snapshots: three flat weeks signal unexplored holes, while the climber is still finding new bins.
A checker audit in miniature. The DUT asserts full one cycle late; a pass-rate style check stays green while a cycle-exact check flags the planted difference.
module late_full_fifo #(parameter int DEPTH = 4) (
input logic clk,
input logic rst_n,
input logic push,
output logic full
);
localparam int CW = $clog2(DEPTH + 1);
logic [CW-1:0] count;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= '0;
full <= 1'b0;
end else begin
if (push && !full) count <= count + 1'b1;
full <= (count == DEPTH); // planted bug: full lags the count by one cycle
end
end
endmodule