Skip to content

UVM

Assertion / Coverage Synergy

Uniting assertions and coverage — cover properties that measure whether assertions actually fired their checks, the two kinds of assertion coverage (antecedent and scenario), how assertion coverage feeds the closure picture, why a vacuous pass is exactly an assertion-coverage gap, and how assertions and coverage together close the loop on both correctness and completeness of the protocol checking.

Assertions in UVM · Module 21 · Page 21.5

The Engineering Problem

This module built the assertion layer — integrated (21.1), written (21.2), positioned as a methodology (21.3), and debugged (21.4). But a recurring thread has been unresolved: an assertion that passes in simulation might have checked nothing — a vacuous pass (21.1, 21.3, 21.4), where the antecedent never fired. A team that signs off the assertion suite on "all assertions passing, green" can ship a protocol bug that a critical assertion was written to catch — because that assertion passed vacuously, never exercised, never actually checking. The missing piece is measurement: how do you know an assertion actually fired its check, and not merely passed because its scenario never occurred? The answer unites this module with Module 19's coverage: assertion coveragecover propertymeasures whether the assertion's scenario was exercised. The protocol layer has the same two orthogonal questions as the transaction layer (Module 19.1): correctness (do the rules hold? — assertions) and completeness (were the rules exercised? — assertion coverage). The problem this chapter solves is the synergy: how cover properties measure that assertions fired their checks, how assertion coverage feeds closure, how vacuous passes are coverage gaps, and how assertions and coverage together close the loop on both correctness and completeness of the protocol checking.

Assertion/coverage synergy is the union of the two orthogonal questions for the protocol/assertion layer: assertions check correctness (do the protocol rules hold?), and assertion coverage (cover property) checks completeness (were the rules actually exercised?). A cover property measures whether a sequence or antecedent occurred — answering "did this assertion actually check?". There are two kinds: antecedent coverage (cover property (req) — did the triggering condition occur, so the assertion ran, not vacuously?) and scenario coverage (cover property (req ##[1:3] ack) — did the full interesting protocol sequence occur?). Assertion coverage feeds the closure picture alongside functional and code coverage (Module 19.6/19.7) — a closed verification requires the assertions' antecedents/scenarios covered, proving the protocol checking was exercised. Crucially, a vacuous pass (21.4) is exactly an assertion-coverage gap — a 0-hit antecedent cover — detected systematically (not by hoping) and closed by the coverage-feedback loop (Module 20.4): steer stimulus to exercise the uncovered antecedent. So closing the assertion layer requires both: all assertions passing (correctness) AND all assertion coverage hit (completeness) — mirroring the scoreboard + functional coverage relationship for the signal/protocol layer. This chapter is the synergy: assertion coverage, its two kinds, feeding closure, vacuous-pass-as-gap, and closing the loop on correctness and completeness.

How do assertions and coverage unite — how does a cover property measure that an assertion actually fired its check, what are the two kinds of assertion coverage, why is a vacuous pass exactly an assertion-coverage gap, and how do assertions (correctness) and assertion coverage (completeness) together close the loop on the protocol checking?

Motivation — why passing assertions aren't enough

A passing assertion is necessary but not sufficient — the same lesson as passing tests (Module 19.1), now for the assertion layer. The reasons coverage must accompany assertions:

  • A passing assertion might have checked nothing. A vacuous pass (antecedent never fired) reports green while verifying nothing. Without coverage, you can't tell a real pass (rule exercised and held) from a vacuous one (rule never exercised).
  • Correctness and completeness are orthogonal — for assertions too. Assertions answer "do the rules hold?" (correctness); they say nothing about "were the rules exercised?" (completeness). The protocol layer needs bothexactly as the transaction layer needs scoreboard + functional coverage (19.1).
  • Vacuous passes are detectable, not just feared. Assertion coverage (covering the antecedents) turns the vacuous-pass worry into a measurable gap: a 0-hit antecedent cover names the un-exercised assertion systematically.
  • Assertion coverage is part of closure. A defensible closure (19.7) includes assertion coverageproving the protocol checking was exercised, not vacuously green. Signing off on passing assertions alone is date-gated-style false closure for the assertion layer.
  • Cover properties also measure protocol-scenario coverage. Beyond antecedents, cover property on interesting sequences (back-to-back, error-during-burst) measures the protocol scenarios exercised — feeding the functional coverage picture from the signal/protocol angle.

The motivation, in one line: a passing assertion is the assertion-layer "passing test"necessary but not sufficient, because a vacuous pass checks nothing; so the protocol layer needs both correctness (assertions hold) and completeness (assertion coverage — the rules exercised), with assertion coverage turning the vacuous-pass worry into a measurable gap and feeding the closure picture — uniting assertions and coverage on the same two orthogonal questions as the scoreboard and functional coverage.

Mental Model

Hold assertion/coverage synergy as the sentry and the log of who approached the post — no alarms is only reassuring if the log shows the post was actually tested:

An assertion is a sentry that sounds an alarm when a rule is broken — that's correctness, the checking. But a sentry that reports no alarms could mean two very different things: either people approached the post and were correctly cleared (the rule was exercised and held), or no one ever approached the post at all (the rule was never tested). You cannot tell these apart from the absence of alarms. So you keep a second record: a log of whether anyone ever approached the post — that's the cover property, the completeness measurement. Now no alarms plus a log showing the post was approached and tested means real assurance: the rule was exercised and it held. But no alarms plus a log showing no one ever approached means the sentry verified nothing — a vacuous pass, false assurance. Closing out the post requires both: no alarms, and a log proving the post was actually tested. A guard force signed off on no-alarms alone has posts that may never have been challenged, and the one unchallenged post is exactly where the intruder walks through. Picture each assertion as a sentry that sounds an alarm when its rule is broken — that's correctness, the checking. But a sentry reporting no alarms could mean two very different things: either people approached the post and were correctly cleared (the rule was exercised and held), or no one ever approached the post at all (the rule was never tested). You cannot tell these apart from the absence of alarms. So you keep a second record: a log of whether anyone ever approached the post — that's the cover property, the completeness measurement. Now no alarms plus a log showing the post was approached and tested means real assurance (the rule was exercised and it held). But no alarms plus a log showing no one ever approached means the sentry verified nothing — a vacuous pass, false assurance. Closing out the post requires both: no alarms, and a log proving the post was actually tested. A guard force signed off on no-alarms alone has posts that may never have been challenged — and the one unchallenged post is exactly where the intruder walks through (the un-exercised assertion guarding the shipped bug). This is the same two-record discipline as the transaction layer (Module 19.1): the scoreboard (alarm) plus functional coverage (the log) — here it's the assertion (alarm) plus assertion coverage (the log), for the signal/protocol posts.

So assertion/coverage synergy is the sentry and its approach-log: the assertion (sentry) checks correctness (rule held — no alarm), and the cover property (log) measures completeness (rule exercised — the post was approached). No alarms alone is ambiguousreal assurance (exercised-and-held) or vacuous (never-tested) — and only the log (cover property) distinguishes them. Closing a post requires both: passing (no alarm) AND covered (the post was tested). Measure both — did the rule hold, and was the rule exercised — because no-alarms on an untested post is false assurance.

Visual Explanation — the two questions for the protocol layer

The defining picture is the parallel: the protocol layer asks the same two orthogonal questions as the transaction layer (19.1) — correctness (assertions) and completeness (assertion coverage).

Protocol layer: correctness (assertions) and completeness (assertion coverage), orthogonaldo the rules hold?were they exercised?assertionsassertion coverage (cover property)assertioncoverage (cover…both → confidenceboth → confidenceProtocol checkingtwo questionsCorrectnessdo the rules hold?Completenesswere they exercised?Assertionsfire on violationAssertion coveragecover propertyConfidenceneeds BOTH12
Figure 1 — the two orthogonal questions, now for the protocol layer. Correctness: do the protocol rules hold? — answered by assertions. Completeness: were the protocol rules actually exercised? — answered by assertion coverage (cover properties). These are orthogonal, exactly like the scoreboard and functional coverage at the transaction layer. An assertion answers only correctness; a cover property answers only completeness. Confidence in the protocol checking requires both — the rules hold AND the rules were exercised. A passing assertion with no coverage checked nothing.

The figure shows the two orthogonal questions for the protocol layer. Correctness: do the protocol rules hold? — answered by assertions (fire on violation). Completeness: were the protocol rules actually exercised? — answered by assertion coverage (cover property). The crucial reading is the exact parallel to Module 19.1's transaction layer: there, the scoreboard answered correctness and functional coverage answered completeness; here, assertions answer correctness and assertion coverage answers completeness — the same two orthogonal questions, now for the signal/protocol layer. They're orthogonal: an assertion answers only correctness (does the rule hold on what was exercised?), a cover property answers only completeness (was the rule exercised?). Neither alone gives confidence in the protocol checking: assertions without coverage leave you unable to tell a real pass from a vacuous one (passing but checked nothing); coverage without assertions measures exercise but checks no correctness. Confidence (the warning-colored outcome) requires boththe rules hold (assertions) AND the rules were exercised (assertion coverage). The brand-colored questions map to the success-colored answers (assertions, assertion coverage), both feeding confidence. This is the unifying insight of the chapter: the protocol layer is not special — it has the same correctness/completeness structure as the transaction layer, and the same requirement for both halves. A passing assertion with no coverage is exactly the protocol-layer form of "passing tests with low coverage" (19.1) — green but unverified. The diagram is the two-question parallel: correctness (assertions) ⊥ completeness (assertion coverage), both required — the transaction-layer structure (scoreboard + functional coverage) repeated for the protocol layer. Assertions check the rules hold; assertion coverage checks the rules were exercised — both, for confidence in the protocol checking.

RTL / Simulation Perspective — cover properties measuring the checks

In code, assertion coverage is cover property — on the antecedent (did the assertion run?) and on the scenario (did the interesting sequence occur?). The example shows assertions paired with their coverage.

pairing each assertion with cover properties — antecedent and scenario coverage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// === THE ASSERTION: correctness — does the handshake rule hold? ===
a_handshake: assert property (@(posedge clk) disable iff (!rst_n) req |-> ##[1:3] ack);
 
// === ANTECEDENT COVERAGE: did the assertion actually RUN (not vacuously pass)? ===
c_req_seen:  cover property (@(posedge clk) req);
//   0 hits → the antecedent NEVER fired → a_handshake passed VACUOUSLY → a coverage GAP (21.4)
 
// === SCENARIO COVERAGE: did the full INTERESTING protocol sequence occur? ===
c_handshake_exercised: cover property (@(posedge clk) req ##[1:3] ack);  // the req-then-ack scenario
c_ack_at_max:          cover property (@(posedge clk) req ##3 ack);      // ack at the MAX latency (corner)
c_back_to_back:        cover property (@(posedge clk) ack ##1 req);      // back-to-back (stress corner)
 
// === ASSERTION COVERAGE FEEDS CLOSURE alongside functional + code coverage (Module 19.6/19.7) ===
//   closure requires:  a_handshake PASSING (correctness)  AND  c_req_seen + scenarios HIT (completeness)
 
// ✗ MISTAKE: sign off on "a_handshake PASSING" with c_req_seen at 0 hits → the assertion checked NOTHING
//            → the protocol scenario was never exercised → the bug it guards SHIPS (DebugLab)

The code shows assertions paired with their coverage. The assertion (a_handshake) is correctnessdoes the handshake rule hold? Antecedent coverage (c_req_seen: cover property (req)) is completeness of the check itselfdid the assertion actually run? A 0-hit c_req_seen means the antecedent never fired, so a_handshake passed vacuously — a coverage gap (21.4). Scenario coverage (c_handshake_exercised: cover property (req ##[1:3] ack), c_ack_at_max: ... req ##3 ack, c_back_to_back: ... ack ##1 req) measures whether the full interesting protocol sequences occurred — the req-then-ack scenario, the ack at max latency corner, the back-to-back stress. Assertion coverage feeds closure alongside functional and code coverage (Module 19.6/19.7) — closure requires a_handshake passing (correctness) AND c_req_seen plus the scenarios hit (completeness). The mistake (commented) is signing off on "a_handshake passing" with c_req_seen at 0 hits — the assertion checked nothing, the protocol scenario was never exercised, and the bug it guards ships (the DebugLab). The shape to carry: each meaningful assertion is paired with cover properties — at least the antecedent (to catch vacuous passes) and often the interesting scenarios (to measure protocol-scenario coverage). The antecedent cover is the vacuous-pass detector: it turns the "did this assertion check?" worry into a measured number (c_req_seen hits). The scenario covers are protocol-scenario coverage — the signal/protocol angle on functional coverage (the corners like max-latency ack and back-to-back). And closure of the assertion layer gates on both: assertions passing (no violations) AND covers hit (the rules and scenarios exercised). Pair every assertion with antecedent and scenario coverage — passing tells you it held, the cover tells you it ran.

Verification Perspective — passing-and-covered versus passing-but-vacuous

The decisive distinction is among passing assertions: passing-and-covered (real assurance) versus passing-but-uncovered (vacuous, false assurance). Seeing the quadrants clarifies what closure requires.

Assertion states: passing-and-covered (closed), passing-uncovered (vacuous), firing (a catch)exercised and heldnever exercised → checked nothingnever exercised→ checked…exercised, violation foundexercised,violation…Passing + coveredantecedent fired, no violationReal assurance (closed)rule exercised AND heldPassing + uncoveredantecedent never firedVacuous — falseassurancea coverage gap (not closed)Firingantecedent fired, violationA real catchdebug it (Module 21.4)12
Figure 2 — the assertion states: passing-and-covered versus passing-but-vacuous, and firing. Passing and covered: the rule was exercised and held — real assurance, the closed state. Passing but uncovered (antecedent never fired): the rule was never exercised, so the assertion verified nothing — a vacuous pass, false assurance, an assertion-coverage gap. Firing: the rule was exercised and a violation was found — a real catch to debug (Module 21.4). Closure of an assertion requires passing AND covered; a passing-but-uncovered assertion is not closed, because it checked nothing.

The figure shows the assertion states — and why closure requires both passing and covered. Passing and covered (the brand-colored top): the antecedent fired (the rule was exercised) and there was no violation (the rule held) → real assurance, the closed state. Passing but uncovered (the warning-colored middle): the antecedent never fired, so the rule was never exercised and the assertion verified nothing → a vacuous pass, false assurance, an assertion-coverage gapnot closed. Firing (the brand-colored bottom): the antecedent fired and a violation was found → a real catch to debug (Module 21.4). The verification insight is that "passing" splits into two very different states — and only coverage distinguishes them. Passing-and-covered is what you want (exercised and held); passing-but-uncovered is the trap (looks green, checked nothing). Without assertion coverage, both report "passing" and are indistinguishable — so a verifier cannot tell a verified rule from an un-exercised one. This is exactly the transaction-layer lesson (19.1's quadrants: checked-and-covered vs checked-but-not-covered) for the assertion layer: the checked-but-not-covered quadrant (passing, vacuous) is false confidence. So closure of an assertion requires both: passing (correctness — no violation) AND covered (completeness — the antecedent/scenario exercised). A passing-but-uncovered assertion is not closed — it's a coverage gap, just like an unhit functional bin. The brand-colored passing-and-covered (and firing) are the resolved states (closed, or a catch to debug); the warning-colored passing-but-uncovered is the unresolved gap. The figure is the closure criterion for an assertion: passing AND covered → closed; passing but uncovered → a vacuous gap; firing → a catch to debug — and only passing-and-covered is real assurance. A passing assertion is closed only if it's also covered; passing-but-uncovered is a vacuous gap that checked nothing.

Runtime / Execution Flow — a vacuous pass is a coverage gap, closed by the loop

At run time, the vacuous-pass-as-coverage-gap insight connects to the coverage-feedback loop (Module 20.4): a 0-hit antecedent is a gap that stimulus steering closes. The flow shows the connection.

Vacuous pass routed through the coverage-feedback loop: steer stimulus to the antecedentantecedent cover = 0 hits → vacuous pass (checked nothing) → an assertion-coverage gap = a stimulus gap → steer stimulus to exercise the antecedent → re-run → cover hit, assertion now checksantecedent cover = 0 hits → vacuous pass (checked nothing) → an assertion-coverage gap = a stimulus gap → steer stimulus to exercise the antecedent → re-run → cover hit, assertion now checks1Antecedent cover = 0 hitsthe cover property on the assertion's antecedent was never hit —the assertion passed vacuously.2It's a coverage gap = a stimulus gapthe antecedent's scenario was never generated, so the assertionchecked nothing — exactly an unhit bin.3Steer the stimulus to itbias constraints or add a directed test to exercise the antecedent— the coverage-feedback loop (Module 20.4).4Re-run: cover hit, assertion checksthe antecedent now fires, the cover is hit, and the assertionactually evaluates its rule on the scenario.
Figure 3 — a vacuous pass is an assertion-coverage gap, closed by the coverage-feedback loop. The antecedent cover for an assertion shows 0 hits, which means the assertion passed vacuously and checked nothing. That is exactly a coverage gap — and it is also a stimulus gap, because the antecedent's scenario was never generated. So it routes into the same feedback loop as any coverage gap: steer the stimulus (bias constraints or add a directed test) to exercise the antecedent, re-run, and confirm the cover is now hit and the assertion actually checked. The fix is stimulus, not the assertion.

The flow shows that a vacuous pass is a coverage gap closed by the feedback loop. Zero hits (step 1): the cover property on the assertion's antecedent was never hit — the assertion passed vacuously. Gap (step 2): it's a coverage gap = a stimulus gap — the antecedent's scenario was never generated, so the assertion checked nothing, exactly like an unhit functional bin. Steer (step 3): bias constraints or add a directed test to exercise the antecedent — the coverage-feedback loop (Module 20.4). Confirm (step 4): re-run — the antecedent now fires, the cover is hit, and the assertion actually evaluates its rule on the scenario. The runtime insight is that the vacuous pass is not a separate problem with its own fix — it's the same kind of gap as any coverage gap, routed through the same loop. This is the deep synergy: assertion coverage (the antecedent/scenario covers) is just more coverage, analyzed and steered exactly like functional coverage (Module 19.6, 20.4). A 0-hit antecedent is ranked, classified, and steered toward alongside unhit functional binsbias the randomization (or add a directed test) to exercise the antecedent, re-run, and confirm the cover is hit (the assertion now checks). The crucial point is that the fix is stimulus, not the assertion — the assertion is fine; it just needs its triggering scenario exercised (Module 21.4). So the assertion layer folds into the coverage-driven methodology: assertions provide correctness checks, their cover properties provide completeness measurement, and the gaps (vacuous passes) drive the same feedback loop that closes functional coverage. The flow is the fold-in: 0-hit antecedent → coverage gap = stimulus gap → steer → re-run → covered — the vacuous pass closed by the coverage-feedback loop, no special handling. A vacuous pass is just a coverage gap — steer stimulus to exercise the antecedent, and the assertion starts checking.

Waveform Perspective — cover properties recording the checks

Assertion coverage is visible as cover properties recording when the antecedent and scenario occur — and where they don't (a gap). The waveform shows the covers firing and an uncovered region.

Cover properties record when the antecedent and the full scenario occur; an uncovered region is a vacuous-pass gap

12 cycles
Cover properties record when the antecedent and the full scenario occur; an uncovered region is a vacuous-pass gapreq fires → antecedent cover (cov_req) hit → the assertion actually RAN herereq fires → antecedent…full req-then-ack sequence completes → scenario cover (cov_scenario) hitfull req-then-ack sequ…later region: req never fires (no_req) → assertion passes VACUOUSLY → covers unhitlater region: req neve…the unhit covers in this region are a visible assertion-coverage GAPthe unhit covers in th…clkreqackcov_reqcov_scenariono_reqt0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — assertion coverage recording the checks. The handshake assertion runs whenever req fires. The antecedent cover (cov_req) records each time req occurs — proving the assertion actually ran there. The scenario cover (cov_scenario) records when the full req-then-ack sequence completes — the interesting protocol scenario exercised. In the early region, req fires and the sequence completes, so both covers are hit and the assertion really checked. In the later region, req never fires (no_req), so the assertion passes vacuously and the covers stay unhit — a visible assertion-coverage gap. Reading the covers tells you where the assertion actually checked versus where it passed vacuously.

The waveform shows assertion coverage recording the checks. The handshake assertion runs whenever req fires. The antecedent cover (cov_req) records each time req occursproving the assertion actually ran there. The scenario cover (cov_scenario) records when the full req-then-ack sequence completes — the interesting protocol scenario exercised. In the early region, req fires and the sequence completes, so both covers are hit and the assertion really checked. In the later region, req never fires (no_req), so the assertion passes vacuously and the covers stay unhit — a visible assertion-coverage gap. The crucial reading is that the cover properties make the assertion's activity visible: cov_req hitting means the assertion ran (the antecedent occurred), and cov_scenario hitting means the full protocol scenario was exercised — so a verifier can see, region by region, where the assertion actually checked (covers hit) versus where it passed vacuously (covers unhit). The unhit covers in the later region are the visible signature of a vacuous pass — the assertion was green there, but it checked nothing (no antecedent), so the covers expose the gap the passing status hides. This is the completeness measurement of the protocol checking: not "did the assertion pass?" (it did, everywhere) but "where did the assertion actually check?" (only where cov_req is hit). The picture to carry is that cover properties are the assertion's coverage — they record when the antecedent and scenario occur, exposing the vacuous regions where the passing assertion verified nothing. Reading the waveform this way — where did the covers fire (real checks) and where are they unhit (vacuous gaps)? — is reading the protocol-checking completeness. The covers hitting in the exercised region and unhit in the no-antecedent region is the signature of assertion coverage doing its job: making visible where the assertion checked and where it didn't. Cover properties record where the assertion actually checked — the unhit regions are the vacuous gaps the passing status hides.

DebugLab — the all-green assertion suite that guarded nothing

A protocol sign-off on 'all assertions passing' that shipped a bug an uncovered assertion was written to catch

Symptom

A team closed the protocol checking on a clean result: the assertion suitedozens of spec-faithful assertions, including one, a_retry_handshake, specifically written to catch a known-tricky retry-then-handshake corner — was entirely passing, millions of cycles, green. The sign-off declared the protocol "fully checked by assertions." The team never looked at assertion coverage. In silicon, a bug surfaced in exactly the retry-then-handshake corner — the scenario a_retry_handshake was built to guard. Reviewing after the fact, the antecedent cover for a_retry_handshake had 0 hits: the retry-then-handshake sequence had never been generated by the stimulus, so the assertion had passed vacuouslygreen, but checking nothing, every cycle. The one assertion that would have caught the bug had never actually run its check, and nobody knew, because the suite was "all passing."

Root cause

The team signed off on correctness alone (all assertions passing) without completeness (assertion coverage) — so a critical assertion that passed vacuously (its antecedent never exercised) was indistinguishable from a real pass, and the protocol scenario it guarded shipped unchecked:

why an all-passing assertion suite still shipped a protocol bug
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
✗ SIGN OFF on "all assertions PASSING" (correctness only, no assertion coverage):
  a_retry_handshake: assert property (...);   // PASSES — millions of cycles, green
  // BUT: cover property (retry_antecedent) = 0 hits → the retry scenario was NEVER generated
  //      → a_retry_handshake passed VACUOUSLY → it checked NOTHING, every cycle
  // "all passing" is INDISTINGUISHABLE from "all vacuous" without coverage
  // the retry-handshake bug ships, uncaught by the assertion written to catch it
 
✓ GATE closure on BOTH passing AND covered (correctness AND completeness):
  c_retry_seen: cover property (retry_antecedent);   // pair every critical assertion with its cover
  // closure requires: a_retry_handshake PASSING  AND  c_retry_seen HIT
  // c_retry_seen at 0 → NOT closed → a coverage gap → steer stimulus to the retry scenario (20.4)
  // re-run: retry scenario exercised → assertion ACTUALLY checks → catches the bug before tapeout

This is the passing-but-uncovered bug — the capstone assertion/coverage failure, and the protocol-layer form of "passing tests with low coverage" (Module 19.1). The team closed the protocol checking on "all assertions passing"correctness onlywithout checking assertion coverage. The critical assertion a_retry_handshake, written to catch a known-tricky corner, passed vacuously: its antecedent (the retry-then-handshake sequence) was never generated by the stimulus, so it checked nothing, every cycle, while reporting green. Without assertion coverage, "all passing" is indistinguishable from "all vacuous" — a suite that checked everything looks identical to one that checked nothing. So the retry-handshake bug shipped, uncaught by the very assertion written to catch it, because that assertion never actually ran its check and nobody knew. The root error is signing off on correctness alone: passing assertions answer "do the rules hold?" but not "were the rules exercised?" — and a vacuously-passing critical assertion is a silent hole. The fix is to gate closure on both passing AND covered: pair every critical assertion with its antecedent cover (c_retry_seen), and require a_retry_handshake passing AND c_retry_seen hit. A 0-hit c_retry_seen means not closed — a coverage gap — so steer the stimulus to the retry scenario (Module 20.4), re-run, and now the assertion actually checks and catches the bug before tapeout. The general lesson, and the chapter's (and module's) thesis: an assertion suite is closed only when its assertions both pass AND are coveredpassing alone is the assertion-layer "passing tests with low coverage": a vacuously-passing assertion checked nothing, and without assertion coverage, "all green" is indistinguishable from "all vacuous", so signing off on passing alone ships the bugs the un-exercised assertions were written to catch; measure assertion coverage (cover the antecedents and scenarios), gate closure on both correctness (passing) and completeness (covered), and treat a vacuously-passing critical assertion as an open coverage gap to close with stimulus. An assertion that passed but was never exercised is a guard who reported "all clear" from a post no one ever approached — and the unguarded post is exactly where the bug walks through.

Diagnosis

The tell is a passing assertion suite signed off without assertion coverage. Diagnose passing-but-uncovered:

  1. Check antecedent coverage for every critical assertion. A 0-hit antecedent cover means the assertion passed vacuously and verified nothing.
  2. Don't trust 'all passing' alone. Without coverage, all-passing is indistinguishable from all-vacuous; require coverage to distinguish them.
  3. Map shipped protocol bugs to assertion coverage. A bug in a scenario whose assertion exists but has 0 antecedent coverage is a vacuous-pass escape.
  4. Audit critical-corner assertions specifically. Rare, tricky scenarios are exactly where the antecedent may never be generated, so their assertions pass vacuously.
Prevention

Gate closure on both passing and covered:

  1. Pair every meaningful assertion with antecedent coverage. A cover property on the antecedent confirms the assertion actually ran, catching vacuous passes.
  2. Cover the interesting protocol scenarios. Cover properties on corners (max-latency, back-to-back, error-during-burst) measure protocol-scenario completeness.
  3. Require passing AND covered for closure. An assertion is closed only when it passes and its antecedent/scenario is hit; a 0-hit cover is an open gap.
  4. Steer stimulus to uncovered antecedents. Treat a vacuous pass as a coverage gap and route it through the feedback loop to exercise the scenario.

The one-sentence lesson: an assertion suite is closed only when its assertions both pass AND are covered — passing alone is the assertion-layer 'passing tests with low coverage', because a vacuously-passing assertion checked nothing and 'all green' is indistinguishable from 'all vacuous' without coverage, so measure assertion coverage, gate closure on both correctness and completeness, and steer stimulus to close any vacuous-pass gap.

Common Mistakes

  • Signing off on 'all assertions passing' without coverage. Passing alone can't distinguish real checks from vacuous passes; require assertion coverage too.
  • Not covering antecedents. Without an antecedent cover, a vacuously-passing assertion looks identical to a real one; cover every meaningful assertion's antecedent.
  • Treating assertion coverage as separate from closure. Assertion coverage is part of the closure picture alongside functional and code coverage; gate sign-off on it.
  • Changing the assertion to fix a vacuous pass. The assertion is fine; the antecedent was never exercised — steer stimulus, don't alter the assertion.
  • Ignoring protocol-scenario coverage. Cover properties on corners measure protocol completeness from the signal angle; skipping them leaves protocol gaps.
  • Forgetting the parallel to functional coverage. The protocol layer has the same correctness/completeness pair as the transaction layer; treat assertion coverage like functional coverage.

Senior Design Review Notes

Interview Insights

Assertion coverage is the measurement, via cover properties, of whether an assertion actually fired its check — whether its antecedent or scenario was exercised — and it's needed alongside assertions because a passing assertion alone can't tell you whether it verified anything or passed vacuously. Assertions answer correctness: do the protocol rules hold? But a passing result is ambiguous. An implication assertion passes vacuously on every cycle its antecedent doesn't hold, so an assertion can report passing while its triggering condition never once occurred — it checked nothing. You can't distinguish a real pass, where the rule was exercised and held, from a vacuous pass, where the rule was never exercised, just from the absence of failures. Assertion coverage resolves this. A cover property measures whether a sequence occurred. There are two kinds. Antecedent coverage — a cover property on the assertion's antecedent, like cover property of req — confirms the assertion actually ran; if it shows zero hits, the assertion passed vacuously. Scenario coverage — a cover property on the full interesting sequence, like req then ack within the window, or corners like ack-at-max-latency and back-to-back — confirms the meaningful protocol scenario was exercised. Together these measure the completeness of the protocol checking, exactly as functional coverage measures completeness at the transaction layer. This is the deep parallel: the protocol layer has the same two orthogonal questions as the transaction layer. Assertions are the correctness check, like the scoreboard; assertion coverage is the completeness measure, like functional coverage. And just as passing tests with low functional coverage is false confidence, passing assertions with no assertion coverage is false confidence — green but unverified. So you pair meaningful assertions with cover properties on their antecedents and scenarios, and you treat assertion coverage as part of closure. The sentry analogy: an assertion is a sentry sounding an alarm on a violation, and the cover property is the log of whether anyone ever approached the post — no alarms is only reassuring if the log shows the post was actually tested.

A vacuous pass is exactly an assertion-coverage gap: the assertion's antecedent was never exercised, which shows up as a zero-hit antecedent cover, and it's also a stimulus gap because the scenario was never generated — so it routes through the same coverage-feedback loop as any coverage gap, and the fix is stimulus, not the assertion. When an assertion passes vacuously, it means its triggering condition never occurred in the tests you ran. If you've paired the assertion with a cover property on its antecedent, that cover will show zero hits — and a zero-hit cover is precisely a coverage gap, identical in kind to an unhit functional coverage bin. So the vacuous pass isn't a mysterious separate problem; it's a measurable gap. And it's specifically a stimulus gap: the antecedent's scenario is reachable, the stimulus just never produced it. That means it routes into exactly the same feedback loop you use for any coverage gap. You analyze it, see the zero-hit antecedent, classify it as a stimulus gap, and steer — bias the constraints or add a directed test to generate the antecedent's scenario. Then you re-run and confirm the cover is now hit, which means the antecedent fired and the assertion actually evaluated its rule on that scenario. Crucially, the fix is stimulus, not the assertion. The assertion is correct; it just needs its triggering scenario exercised. Changing the assertion would be wrong — you'd be altering a correct check to make a coverage problem go away. This is the elegant part of the synergy: assertion coverage is just more coverage, analyzed and steered like functional coverage, and vacuous passes fold into the coverage-driven methodology with no special handling. A zero-hit antecedent is ranked and steered toward alongside unhit functional bins, the same loop closes it, and the result is an assertion that now actually checks. So the relationship is identity: a vacuous pass is a coverage gap, specifically a stimulus gap, closed by exercising the antecedent through the feedback loop.

Closing the assertion layer means every meaningful assertion both passes and is covered — passing for correctness, covered for completeness — and passing alone isn't enough because a vacuously-passing assertion checked nothing, so all-passing is indistinguishable from all-vacuous without coverage. An assertion has three relevant states. Passing and covered: the antecedent fired and there was no violation, so the rule was exercised and held — real assurance, the closed state. Passing but uncovered: the antecedent never fired, so the rule was never exercised and the assertion verified nothing — a vacuous pass, false assurance, an open coverage gap. And firing: the antecedent fired and a violation was found — a real catch to debug. The trap is that the first two both report passing, and without coverage you can't tell them apart. So a suite that's all-passing might be fully verifying the protocol, or might be full of vacuous passes verifying nothing, and the pass result alone doesn't say which. This is the protocol-layer version of passing tests with low functional coverage — the checked-but-not-covered quadrant — which is false confidence. Therefore closing the assertion layer requires both: every meaningful assertion passing, which is correctness, and its antecedent and scenario coverage hit, which is completeness. A passing-but-uncovered assertion is not closed; it's a coverage gap, exactly like an unhit functional bin, to be closed by steering stimulus to exercise it. The danger of getting this wrong is concrete: a critical assertion written to catch a tricky corner can pass vacuously because the stimulus never generates that corner, and if you sign off on all-passing without coverage, the bug in that corner ships, uncaught by the very assertion meant to catch it, because the assertion never actually ran. So closure of the assertion layer is gated on both passing and covered — and assertion coverage is part of the overall closure picture alongside functional and code coverage. The slogan is that an assertion that passed but was never exercised is a guard reporting all clear from a post no one ever approached.

Cover properties measure protocol-scenario coverage by recording when interesting temporal sequences on the signals occur, giving you the signal-and-protocol-level view of completeness that complements transaction-level functional coverage. Beyond covering an assertion's antecedent to catch vacuous passes, you write cover properties on the meaningful protocol sequences you want to ensure were exercised. These are temporal patterns, expressed in the same SVA sequence language as assertions. For a handshake protocol, you might cover the basic completed handshake — req then ack within the window — to confirm the normal scenario occurred. Then you cover the corners: ack at the maximum latency, req then ack at exactly the longest allowed delay, because the boundary is where timing bugs hide. Back-to-back transactions — ack then req on the next cycle — to confirm the pipelined stress case was exercised. Error-during-burst — an error response arriving mid-sequence — to confirm that interaction occurred. Each cover property records when its sequence completes, so at the end you know which protocol scenarios were actually generated. This is protocol-scenario coverage, and it feeds the same closure picture as functional coverage, just from the signal and temporal angle rather than the transaction-field angle. The two are complementary: functional coverage might cover that a write of max size occurred as a transaction, while a cover property covers that the cycle-by-cycle protocol sequence for that write — the handshake timing, the back-to-back relationship — actually happened on the wires. Both are completeness measures, and both go into closure. The value is that some important scenarios are inherently temporal and signal-level — a specific ordering of handshake events, a timing corner — and those are naturally expressed and measured as cover properties on sequences, not as transaction-field bins. So in a mature environment, you have functional coverage on transaction content and cross-coverage on combinations, plus cover properties on the protocol sequences and corners, together measuring whether you exercised both the functional space and the protocol space. Cover properties are how the assertion layer contributes its completeness measurement to the overall coverage closure.

The assertion layer mirrors the transaction layer exactly: assertions are to protocol correctness what the scoreboard is to functional correctness, and assertion coverage is to protocol completeness what functional coverage is to functional completeness — the same two orthogonal questions, correctness and completeness, applied at the signal-and-protocol level instead of the transaction level. At the transaction layer, the scoreboard checks correctness — is the DUT's output right, end-to-end — and functional coverage measures completeness — were the scenarios exercised. These are orthogonal: the scoreboard says nothing about whether you tested enough, and coverage says nothing about whether the results were right. You need both, because passing tests with low coverage is false confidence — you verified what you ran, but maybe ran too little. At the protocol layer, the structure repeats. Assertions check correctness — do the protocol rules hold, cycle by cycle — playing the scoreboard's role for signal-level temporal behavior. Assertion coverage, via cover properties, measures completeness — were the protocol rules and scenarios exercised — playing functional coverage's role. These are equally orthogonal: an assertion passing says nothing about whether its rule was exercised, and a cover property says nothing about whether the rule held. And you need both for the same reason: passing assertions with no coverage is false confidence, because a vacuous pass checked nothing — the protocol-layer form of passing-tests-with-low-coverage. So the mental model is one structure applied at two levels. Each level has a correctness checker and a completeness measure, orthogonal, both required, closed only when both are satisfied. This unifies the whole verification picture: at the transaction level, scoreboard plus functional coverage; at the protocol level, assertions plus assertion coverage; and they layer together — the transaction layer catches functional data bugs and measures functional completeness, the protocol layer catches signal-level protocol bugs and measures protocol completeness. The recurring lesson across both is that correctness and completeness are orthogonal and you need both, and that passing without coverage — at either level — is green but unverified. Recognizing this parallel means everything you learned about functional coverage and closure applies directly to assertions, which is the synthesis this chapter and the coverage module together deliver.

Exercises

  1. Pair the coverage. For a handshake assertion, write the antecedent cover and two scenario covers (a corner and a stress case).
  2. Diagnose the vacuous pass. An assertion passes but its antecedent cover is 0. State what it means, why it's a coverage gap, and the fix.
  3. State the closure criterion. Explain why an assertion is closed only when both passing and covered, and what each half rules out.
  4. Map the parallel. Match the assertion layer's correctness and completeness mechanisms to their transaction-layer counterparts.

Summary

  • Assertion/coverage synergy unites the two orthogonal questions for the protocol layer: assertions check correctness (do the rules hold?), and assertion coverage (cover property) checks completeness (were the rules exercised?) — the same pair as the scoreboard + functional coverage (Module 19.1), now for the signal/protocol layer.
  • A cover property measures whether a sequence occurred: antecedent coverage (cover property (req) — did the assertion run, not vacuously?) and scenario coverage (cover property (req ##[1:3] ack) — were the interesting protocol scenarios exercised?).
  • A vacuous pass is exactly an assertion-coverage gap — a 0-hit antecedent cover — and also a stimulus gap, so it routes through the coverage-feedback loop (Module 20.4): steer stimulus to exercise the antecedent; the fix is stimulus, not the assertion.
  • Closing the assertion layer requires both: all assertions passing (correctness) AND all assertion coverage hit (completeness) — a passing-but-uncovered assertion checked nothing, and "all green" is indistinguishable from "all vacuous" without coverage.
  • The durable rule of thumb: assertions and coverage close the loop on the protocol layer the same way the scoreboard and functional coverage do on the transaction layer — assertions check the rules hold, cover properties check the rules were exercised, and you need both; pair every meaningful assertion with antecedent and scenario coverage, gate closure on passing AND covered, and treat a vacuously-passing assertion as an open coverage gap to close with stimulus, because a passing assertion that was never exercised is a guard reporting 'all clear' from a post no one ever approached.

Next — Why Callbacks Exist: the next module turns to a different challenge — extending reusable components. A vendor VIP or shared agent is sealed, yet each test and team needs a small tweak at a specific point — error injection before driving, logging after receiving. The module opens with why callbacks exist: why modifying the source breaks reuse and whole-type factory override is the wrong tool for a point-tweak, and how callback hooks let users inject behavior at predefined points — additively, with multiple customizations coexisting — without ever forking the component.