Functional Coverage Closure: Why 100% Can Be a Lie
“Coverage is at 100%” is the most reassuring sentence in a verification review, and one of the easiest to mislead with. A coverage number is not a fact about the design — it is a fact about a model you wrote and a denominator you chose, and both can be quietly bent until a half-verified block reports a perfect score. This page explains what functional coverage closure actually means, how it differs from code coverage, how covergroups, coverpoints, crosses, and bins fit together, and the specific traps — rounding, dropped crosses, and parse-excluded signals — that turn a genuine 99.99% into a false 100%. It pairs naturally with your verification plan, which is where every coverage goal should originate.
Code coverage vs functional coverage
Code coverage measures whether the simulator executed the RTL text: line, branch, condition/expression, FSM-state, and toggle coverage. It is collected automatically from the source, so it tells you what the testbench touched — not what it set out to prove. It is great at finding dead code and never-exercised branches and useless at telling you whether you checked the interesting combinations.
Functional coverage measures whether you hit the scenarios you declared in a coverage model written from intent. The two are complementary: 100% code coverage over a thin functional model means every line ran while the meaningful corner cases went unobserved. Real closure needs both numbers, read together, with the functional model traced back to plan items.
Covergroups, coverpoints, and crosses
A covergroup is the functional coverage model. It is sampled on an event (often a clock edge gated by a handshake) and scores the coverpoints inside it. Each coverpoint observes one expression — a packet length, an opcode, a mode — and divides its values into bins. A cross is the Cartesian product of two or more coverpoints, and it is where the real holes live: hitting every value of A and every value of B is not the same as hitting every (A, B) pair.
// A covergroup is the functional coverage model: it is sampled on an
// event and scores the bins you declared. The number it reports is only
// as honest as the bins, crosses, and exclusions written below it.
covergroup cg_packet @(posedge clk iff (valid && ready));
// Coverpoint with auto + explicit bins.
cp_len: coverpoint pkt.len {
bins small = {[1:15]};
bins medium = {[16:127]};
bins large = {[128:1023]};
// Values outside these ranges are silently uncovered unless you
// add a bin for them -- a classic source of a flattering number.
}
cp_port: coverpoint pkt.dst_port {
bins ports[] = {[0:7]};
illegal_bins reserved = {8, 9}; // hitting these fails, not counts
ignore_bins unused = {[10:15]};// excluded from the denominator
}
// Cross coverage: the (len x port) product. This is where the holes
// live -- 100% on cp_len and cp_port can sit over a half-empty cross.
x_len_port: cross cp_len, cp_port;
endgroupBins: cross, ignore, and illegal
- Explicit and auto bins define the buckets a coverpoint scores. Values you never put in a bin are simply uncovered — and if no bin exists for them, they may not even appear in the denominator.
- Cross bins are the product combinations. Their count explodes fast, which is exactly why teams under-declare them and why a high coverpoint score can sit on top of a sparse cross.
- ignore_bins are removed from the denominator — legitimate for genuinely unreachable values, dangerous when used to make an inconvenient number disappear.
- illegal_bins do not count toward coverage at all; hitting one is a failure. They encode “this must never happen,” not “this is covered.”
Toggle coverage
Toggle coverage is a structural form of code coverage: it asks whether each net switched both 0→1 and 1→0 during simulation. It is cheap and good at exposing tied-off control bits and dead stimulus, but it says nothing about meaning — a bit can toggle perfectly and still be wrong. Toggle numbers are also fragile in tooling: some flows silently drop nets that were optimized away or never instantiated, which removes them from the denominator and inflates the percentage. The honest move is to report the raw toggled-vs-total counts, not just a tidy headline figure.
What real closure means
Closure is not the moment a meter reads 100%. It is the point where every coverage goal traces back to a plan item, every gap is either hit or explicitly waived with a reviewed rationale, and the denominator the number was measured against is itself reviewed. A coverage report without its model and its exclusions is a number without provenance. Treat the figure as evidence to interrogate, not a finish line to celebrate — the same discipline a good RTL verification flow applies everywhere else.
The false-100% traps
- Rounding a 99.99% to 100%. A single un-hit bin out of ten thousand rounds to 100.0% in a report that shows one decimal. The last missing case is often the corner you most needed to see. Honest reporting keeps the finite value — 99.99% is not 100%.
- Dropped crosses. If the cross is never declared, or is quietly excluded, the report can show 100% on the individual coverpoints while most (A, B) pairs were never exercised. The number is technically true and practically meaningless.
- Parse-excluded signals. When a tool fails to parse part of the design, or an optimizer removes untoggled nets, those items can vanish from the denominator entirely. Coverage of what was measured reads 100% precisely because the hard part was never measured.
- Vacuous coverage. A covergroup that is never sampled, or sampled under a condition that is always false, can report a non-failing result while observing nothing at all — coverage that is empty, not complete.
How ChipVerify reports coverage
ChipVerify AI instruments your coverage model and runs it on open-source simulators, then reports honest, finite measured coverage: the toggled-vs-total and hit-vs-total counts behind the percentage, never a value rounded up to 100% and never a vacuous result from an un-sampled group. If part of the design did not parse or a group was never sampled, it says so rather than shrinking the denominator to flatter the number. This is pre-signoff, measured evidence on open tools — an input to your closure decision, not a foundry signoff and not a certification.
FAQ
What is the difference between code coverage and functional coverage?
Code coverage measures whether the simulator exercised the RTL text — lines, branches, conditions, toggles, and FSM states. It is collected automatically from the source and tells you what the testbench touched, not what it meant to verify. Functional coverage measures whether the testbench hit the scenarios you declared in a coverage model: covergroups, coverpoints, bins, and crosses written from the verification plan. You need both, because 100% code coverage with a thin functional model means you ran every line without ever checking the interesting combinations.
What is cross coverage in a covergroup?
A cross coverage point is the Cartesian product of two or more coverpoints — for example, packet length crossed with destination port, or opcode crossed with operating mode. The cross enumerates the combinations of bins, so it grows quickly and is where most real coverage holes hide. A covergroup can report a high single-coverpoint number while the cross is mostly empty, because hitting every value of A and every value of B does not mean you hit every (A, B) pair.
Can functional coverage closure replace a commercial signoff flow?
No. Coverage closure is measured evidence that your declared scenarios were exercised; it is one input to a verification plan, not a foundry signoff or a certification. A 100% number is only as strong as the coverage model behind it and the denominator it was measured against. ChipVerify AI reports honest, finite measured coverage on open-source tools as pre-signoff evidence — it never rounds up to 100% and never reports a vacuous result.
Related reading
- Verification plan — where every coverage goal should originate and trace back to.
- Verification waivers — how to exclude a coverage gap honestly, with a reviewed reason.
- RTL verification — where functional coverage sits in the broader flow.
- Tapeout readiness — coverage closure is a line item before a cut.
Measure honest functional coverage on your RTL
Sign in and ChipVerify AI instruments your covergroups and runs them on open-source simulators, returning finite measured coverage with the hit-vs-total counts behind the number — never rounded up to 100%, never vacuous. Pre-signoff measured evidence, not a foundry signoff.