Skip to content

UCIe · Module 20

UCIe Functional Coverage

Proving a UCIe regression exercised the meaningful architectural space rather than merely running many tests — a five-level plan hierarchy, coverage sampled on the event it describes rather than every clock, crosses justified by a named bug, illegal bins that require specification evidence rather than an implementation limit, recovery-context coverage as the centerpiece, and closure that classifies every uncovered bin instead of waiving it.

Chapters 20.1 to 20.4 built everything that decides whether the design is correct: boundary contracts, temporal properties, and obligation models. None of them answers a different and equally necessary question — did the regression ever put the design in the situations those checks were written for?

1. The One-Sentence Model

Functional coverage measures whether meaningful architectural situations were observed. It does not measure correctness. Correctness comes from assertions, scoreboards and reference models; coverage answers did the scenario happen — under which configuration, in which state, with which fault, with which concurrency, and in which recovery context?

That division of labour is absolute, and forgetting it produces both of the classic failures. A plan with excellent coverage and weak checkers proves the design was exercised while wrong. A plan with excellent checkers and no coverage proves the design was correct in whatever situations happened to occur — and 20.3 §48 showed that a property whose antecedent never fires passes 100% of a regression.

2. What This Chapter Owns

QuestionWhere it is answered
Boundary contracts, the five verification planes, independent models, safety versus liveness20.1 — Protocol Verification
The link lifecycle, reference link model, fault injection, recovery scenarios20.2 — Link Verification
Temporal properties, triggers, scoping, vacuity, antecedent covers20.3 — UCIe Assertions
Obligation models, correlation, epochs, first divergence20.4 — UCIe Scoreboards
The UVM environment that hosts these collectors20.6 — UVM Architecture for UCIe
Conformance, interoperability and official compliance20.7 — UCIe Compliance Testing

Earlier chapters wrote activation covers for individual properties (20.3 §67) and scenario covers for individual models (20.4 §57). This chapter is the model those were fragments of — and the difference is architectural rather than a matter of scale:

A five-level plan hierarchy (§6) that keeps requirement coverage, scenario coverage, state coverage, cross coverage and code coverage from being confused with one another — because three of the five get quoted as if they were the first.

Sampling on the event a bin describes (§9–§10). A lifecycle bin sampled every clock measures residency, not scenario diversity, and reports 99% for the state the link idles in.

Crosses that must name a bug (§31–§33). The Cartesian product of every dimension is millions of bins, most impossible, and closure becomes bin-count management.

Illegal bins that require specification evidence (§13–§14) — because an illegal_bins derived from what the current RTL cannot do has encoded an implementation limit as a requirement, which is 20.4 §16's mirroring failure in the coverage model.

Recovery-context coverage as the centerpiece (§26–§27), because "a recovery occurred" is the bin almost every plan has and almost none of the questions it needs to answer.

And closure that classifies rather than waives (§50–§52). Six classifications, each with a different owner and a different next action, and "never hit in regression" is not a proof of unreachability.

3. Sourcing

4. Coverage Answers a Different Question

Two questions, two mechanisms, and neither substitutes for the other.

Checkers — assertions, scoreboards, modelsCoverage
Askswas the behaviour correct?did the situation occur?
Fails whenthe design broke a contractnothing — coverage never fails
Reportsa defecta gap in the evidence
Silent whenthe situation never arosethe situation arose and was wrong
Owner of the gapthe design teamthe stimulus, the environment, or the plan

Three consequences.

Coverage cannot fail a test, and that is not a weakness. It is a measurement, and treating an uncovered bin as a defect confuses the measurement with the thing measured. §50's classification exists precisely because an uncovered bin has six possible owners and only one of them is the design.

A green regression with no coverage model is an unquantified claim. Every property passed — on the situations that happened. 20.3 §48's vacuity argument is coverage's argument in miniature, and this chapter is that argument applied to the whole plan.

And a covered bin with a weak checker is worse than an uncovered one. The situation occurred, nothing checked it, and the coverage report now records it as evidence. §34's pairing rule is the defence, and §58's review table makes the pairing visible.

5. Coverage Is Not a Score

100% of a poor model proves very little. 80% of a carefully defined model is frequently the more informative result — because the uncovered 20% is a list of specific, named questions.

What an uncovered bin can mean, and they are not the same finding:

MeaningOwnerNext action
the stimulus never produced itDVwrite a sequence or a constraint
the environment prevents itDVfix the environment restriction
the design prevents it because of a bugdesigna defect, found by coverage
it is architecturally unreachablearchitectureprove it (§51), then exclude
the requirement is obsoletearchitectureremove the requirement and the bin
the coverage model is wrongDVfix the bin definition

Two observations.

Row 3 is coverage finding a design bug, and it is the row people forget coverage can do. A bin that should be reachable and is not — a degraded width the architecture supports and the design cannot enter — is a defect discovered by a mechanism that never fails a test.

And rows 4 to 6 are the rows that get skipped. A bin nobody can hit is waived, and the waiver records neither which of the three it was nor what evidence supports it. §52 is the record that makes the difference.

Read the model, not the number. A percentage is a summary of a model whose quality it cannot express.

6. The Five-Level Plan Hierarchy

Five levels, and three of them routinely get quoted as if they were the first.

LevelMeasuresDerived fromAnswers
1 — Requirementeach architectural requirement was checked and exercisedthe architecture / specificationdid we verify what we promised to verify?
2 — Scenarioeach meaningful situation occurredthe verification plandid the interesting things happen?
3 — State / configurationeach meaningful state and configuration was enteredthe design's legal spacedid we run in every mode?
4 — Crossmeaningful combinations occurredpairs whose interaction can break (§31)did the interactions happen?
5 — Code / structuralstatements, branches, toggles, FSM arcs were executedthe RTLis any logic unexercised?

Four rules that follow from the ordering.

Level 1 is the only one that can be complete. The requirement list is finite and enumerable; the state space is not. A plan that reports levels 3 to 5 and not level 1 has measured what it happened to touch rather than what it set out to verify.

Level 5 is evidence, never a requirement substitute (§36). It answers a question about the implementation; levels 1 and 2 answer questions about the architecture.

Level 2 is where UCIe verification lives. "A recovery with three outstanding obligations and zero credit" is a scenario; it is not a state, not a configuration, and not expressible as a single coverpoint. §26 is the level-2 model for recovery, and it is the chapter's centerpiece.

And they must be reported separately. A merged number across all five is uninterpretable — level 5 is easy to drive high and level 2 is not, so a combined figure is dominated by the level that matters least.

7. Requirement-to-Coverage Mapping

The level-1 artefact: every architectural requirement answers both questions.

Requirement IDs below are this chapter's own labels, not UCIe identifiers (§3). They exist so the table can be referenced.

IDRequirement (illustrative architecture)CheckerCoverage itemNegative testBasis
R1traffic is admitted only when operational and a configuration is agreed20.2 §13lifecycle transition + config-agreed cross (§11, §16)admit during trainingarchitecture
R2the committed configuration matches an independent negotiation rule20.2 §20negotiation-outcome bins (§16)incompatible capabilityarchitecture
R3a semantic operation is delivered exactly once20.3 §33attempts-per-operation bins (§28)duplicate arrivalarchitecture
R4a corrupt object is never delivered20.3 §32verdict bins × delivery (§24)corrupt after integrityarchitecture
R5semantic obligations survive a transport recovery20.3 §38outstanding-at-recovery bins (§26)recovery with workarchitecture
R6credit never exceeds active capacity20.3 §28credit boundary bins (§23)duplicate returnarchitecture
R7a stale-epoch event changes nothing20.3 §38stale-epoch bins (§23)straggler after recoveryarchitecture
R8the active configuration changes atomically20.3 §37commit-under-traffic bins (§16)commit mid-operationarchitecture
R9no resource is released before its obligation resolves20.4 §25stranded-check activationfree on sendarchitecture
R10a degraded configuration is legal for current resources20.2 §28width before × after (§18)lane loss with workarchitecture
R11no traffic class is starved20.3 §45per-class service bins (§19)sustained one-class loadarchitecture
R12every accepted obligation reaches a terminal state20.2 §47terminal-classification bins (§26)fatal with workarchitecture

Three properties of this table.

Every row has a checker and a coverage item. A row with a checker and no coverage item is a check that may never have run; a row with coverage and no checker is a situation that was exercised and unverified. Both are gaps and the table makes both visible.

Every row has a negative test. 20.1 §44's rule: a requirement whose violation has never been attempted has an unexercised detection path.

And the basis column is where §3's discipline lands. Every row here reads "architecture" — not one is claimed as a UCIe normative requirement. A real plan's basis column would cite specification clauses, and the column existing is what forces the citation.

8. Covergroup Architecture

Six collectors, not one covergroup. The split is by responsibility, and it mirrors 20.4 §5's model split for the same reason.

CollectorSamples onOwns
link_covlifecycle eventsphase transitions, training attempts, recovery outcomes
config_covconfiguration commitsnegotiated outcomes, widths, protocol enables, epochs
resource_covcredit and occupancy eventscredit boundaries, occupancy buckets, stranding
reliability_covverdicts, attempts, deliveriesfault families, attempt counts, duplicate paths
recovery_covrecovery entry and exitrecovery context — the centerpiece (§26)
traffic_covacceptance and completionclasses, directions, concurrency, ordering

Three reasons this beats one covergroup.

Each samples a different event (§9). One covergroup has one sample call, so it must either sample on a union of events — making most coverpoints stale at most samples — or sample every clock, which is §10.

Each can be enabled independently. A long run frequently disables the expensive collectors, and a monolith cannot be partially disabled — the same argument 20.4 §4 makes about scoreboards.

And an uncovered bin names its collector, which is the first step of §50's classification: a gap in recovery_cov and a gap in config_cov have different owners and different next actions.

9. Sample on the Event the Bin Describes

The single most consequential decision in a coverage model.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Each collector samples on ITS OWN semantic event, delivered by
// a monitor (20.4 §9). No collector samples on a bare clock.
class ucie_config_cov;
  covergroup cg_config;
    // Sampled at a CONFIGURATION COMMIT — once per commit, not once per cycle.
    cp_negotiation: coverpoint neg_outcome {
      bins full       = {NEG_FULL};
      bins degraded   = {NEG_DEGRADED};
      bins none       = {NEG_NONE};          // the refusal — §16
    }
    cp_width_class: coverpoint width_class {
      bins w[] = {[WC_MIN:WC_MAX]};          // symbolic classes — §17
    }
    cp_commit_context: coverpoint outstanding_bucket {
      bins idle = {OB_ZERO};
      bins busy = {[OB_ONE:OB_MANY]};        // §16 — the case that matters
    }
  endgroup
 
  function void on_event(ucie_event e);
    if (e.kind == EVT_CFG_COMMIT) begin
      neg_outcome       = classify_negotiation(e);
      width_class       = classify_width(e);
      outstanding_bucket = bucket(sem_model.outstanding_count());
      cg_config.sample();                    // exactly once per commit
    end
  endfunction
endclass

The rule, per collector:

QuestionSample on
which configurations were used?the configuration commit
which protocol classes were carried?the acceptance of an object of that class
which recovery contexts occurred?recovery entry (context) and exit (outcome)
which fault families were exercised?the injection, and separately the detection
which lifecycle transitions happened?the transition (§12)
which occupancy states were reached?a push or a pop — the events that change it

And the rule's justification is one sentence. A bin is a claim that a situation occurred; sampling on an unrelated event turns the count into a measure of how long that situation persisted, which is a different quantity with a misleading name (§10).

10. Wrong Coverage — Clock-Sampled State

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — the lifecycle sampled every clock.
covergroup cg_link_bad @(posedge clk);
  cp_phase: coverpoint link_phase {
    bins reset = {RM_RESET};       bins training = {RM_TRAINING};
    bins oper  = {RM_OPERATIONAL}; bins recovery = {RM_RECOVERY};
  }
endgroup

Worked, with realistic durations. A run trains for 1,000 cycles, operates for 100,000, and recovers twice for 500 cycles each.

BinHitsShare
reset200.02%
training1,0000.98%
oper100,00098.0%
recovery1,0000.98%

All four bins are hit. Coverage reports 100%. And the report says almost nothing.

Four properties.

It measures residency, not scenario diversity. The 98% figure describes how long the link idled in its operating state — which is a performance observation dressed as a coverage result.

Two recoveries and two hundred recoveries produce nearly identical reports. The hit count scales with duration, so a run with one long recovery looks better covered than a run with fifty short ones. The quantity the plan cares about — how many distinct recoveries occurred, in how many distinct contexts — is not measured at all.

It hides the missing transitions. All four states were entered; nothing says whether RECOVERY → OPERATIONAL ever happened as opposed to RECOVERY → FAILED twice. §12 is what does.

And it makes closure meaningless. The bins fill within the first thousand cycles of any run. A coverage item that is satisfied by the smoke test cannot guide stimulus development, which is coverage's main practical use (§55).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Sampled on lifecycle EVENTS from the link model (20.2 §7).
// The phase enumeration is the ENVIRONMENT's five-phase model, deliberately
// not the published UCIe state names (§3, and 20.2 §8's reasoning).
covergroup cg_lifecycle;
  // States entered — each ENTRY counted once, not each cycle of residency.
  cp_phase_entered: coverpoint phase_entered {
    bins reset    = {RM_RESET};      bins training = {RM_TRAINING};
    bins oper     = {RM_OPERATIONAL}; bins recovery = {RM_RECOVERY};
    bins failed   = {RM_FAILED};
  }
 
  // Transitions — the level-2 question (§6). Sampled on the transition itself.
  cp_arc: coverpoint phase_arc {
    bins reset_to_train    = {ARC_RESET_TRAIN};
    bins train_to_oper     = {ARC_TRAIN_OPER};
    bins train_restart     = {ARC_TRAIN_TRAIN};     // 20.2 §26
    bins train_to_failed   = {ARC_TRAIN_FAILED};    // 20.2 §24 — the refusal
    bins oper_to_recovery  = {ARC_OPER_RECOVERY};
    bins recovery_to_oper  = {ARC_RECOVERY_OPER};
    bins recovery_to_failed = {ARC_RECOVERY_FAILED}; // 20.2 §42
    bins failed_to_reset   = {ARC_FAILED_RESET};    // the commanded exit
  }
 
  // How many training attempts were needed — a distribution, not a boolean.
  cp_train_attempts: coverpoint train_attempt_count {
    bins first_time = {1};
    bins one_retry  = {2};
    bins several    = {[3:MAX_TRAIN_ATTEMPTS-1]};
    bins exhausted  = {MAX_TRAIN_ATTEMPTS};         // the budget boundary (§22)
  }
endgroup

Four notes.

Eight arcs, and four of them are failure or restart paths. train_to_failed, recovery_to_failed, train_restart and failed_to_reset are the arcs a happy-path regression never takes — and they are where the refusal and classification logic lives (20.2 §24, 20.2 §41).

cp_phase_entered counts entries, not cycles. The same enumeration as §10's wrong version, sampled on a different event, producing a completely different and actually useful measurement.

cp_train_attempts covers the budget boundary. The exhausted bin is the one that exercises §22's boundary discipline and 20.2 §24's bounded-retry termination — and it requires a persistent injected fault, so it will not appear by accident.

And the phase enumeration is the environment's, not the design's. 20.2 §8's principle carried into the coverage model: a coverage model built on the design's state encoding cannot record that the design entered a phase it should not have, because the encoding is the design's opinion. §42 develops this.

12. Transition Coverage, Not Residency

The general rule this chapter keeps returning to:

If the architectural question is about a change, cover the change. If it is about a situation, cover the entry into the situation. Almost never cover the persistence of either.

Architectural questionWrong coverpointRight coverpoint
were all lifecycle paths taken?phase, per clockthe arc, per transition
were all occupancy states reached?occupancy, per clockthe bucket at each push/pop
was credit ever exhausted?credit value, per clockthe transition to zero, and the recovery from it
was the configuration ever changed live?active config, per clockthe commit event, with its context
were both widths used?width, per clockwidth at each commit, plus §18's cross

Two exceptions where residency genuinely is the question.

A duration bin — how long a state persisted — is a legitimate coverage item when the architecture has a bound on it. "Was a recovery ever longer than half the permitted budget?" is a real question, and it is covered by bucketing the duration at the exit event, not by sampling the state every cycle.

And a simultaneity bin is sampled per cycle by necessity. "Did a consume and a return ever coincide?" (19.5 §13) is a per-cycle question, so it samples per cycle — but its bins are the four combinations, not the credit value, so residency does not distort it.

13. Illegal Bins Require Evidence

An illegal_bins is a specification claim. It says the condition must never occur — which means something must have said so.

Acceptable bases, and there are only four:

BasisExample
specification texta state transition the specification forbids
an architectural contract"an identity is not reused while live" (19.2 §21)
a parameter legality rulea class index at or above NUM_CLASSES (19.6 §13)
an arithmetic impossibilityoccupancy exceeding depth, given a correct structure

Unacceptable bases:

  • "the current RTL cannot do that" — §14;
  • "we have never seen it" — that is an uncovered bin, not an illegal one;
  • "the stimulus does not generate it" — an environment restriction (§50, row 2);
  • "it would be a bug" — then it belongs in an assertion, which fails and localises, while an illegal bin only reports a count (§40).

And where the basis is real, prefer the assertion anyway. 20.3 §5's grid: an assertion fails at the cycle, names the boundary, and appears in the failure report. An illegal bin appears in the coverage report at end of test with a count. The illegal bin is supplemental; the assertion is the checker.

14. Wrong Illegal Bin — an Implementation Limit as a Requirement

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — the current design does not support degraded recovery from x8 to x4,
// so the coverage author marks that transition illegal.
covergroup cg_width_bad;
  cp_width_change: coverpoint width_transition {
    bins same        = {WT_SAME};
    bins to_narrower = {WT_NARROWER};
    illegal_bins x8_to_x4 = {WT_X8_TO_X4};      // "we don't support that"
  }
endgroup

Three consequences, in the order they arrive.

The required path is never tested. The architecture permits degradation to that width; the coverage model now forbids exercising it, so no sequence is written and no checker for it is ever exercised.

When the design later supports it, the coverage model fails the run. The illegal bin fires on correct behaviour, and — following 20.3 §14's pattern — the fix applied is usually to delete the bin rather than to reclassify it, taking the coverage item with it.

And the model has encoded an implementation limitation as a requirement. This is 20.4 §16's mirroring failure in the coverage model: the model agreed with the design about what is possible, so it can never record that the design is missing something.

The correct handling depends on which of three situations it actually is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RIGHT — three different answers for three different situations.
covergroup cg_width;
  cp_width_change: coverpoint width_transition {
    bins same        = {WT_SAME};
    bins to_narrower = {WT_NARROWER};
 
    // (a) The architecture permits it and the design does not yet: leave it
    //     UNCOVERED, and let §50 classify it as "DUT prevents — investigate".
    bins x8_to_x4    = {WT_X8_TO_X4};
 
    // (b) This product genuinely does not support the feature (not built —
    //     19.6 §20's capability structure says so): IGNORE, with a reason.
    ignore_bins unsupported_by_this_sku = {WT_X32_TO_X16} with
      (!capability.degraded_width_supported);      // §15
 
    // (c) The architecture forbids it — WIDENING during a recovery, say:
    //     illegal, and there should also be an assertion (§13).
    illegal_bins widening_during_recovery = {WT_WIDER_IN_RECOVERY};
  }
endgroup

The three answers differ in who owns the gap. (a) is a question for the design team, (b) is a documented product scope, (c) is a rule. Collapsing them into one illegal_bins loses all three distinctions.

15. ignore_bins, and Its Documentation Requirement

ignore_bins removes a bin from the denominator. That is a claim, and it needs a record — the same record §52 requires for a waiver, because it is the same thing done earlier.

Three legitimate uses:

Architecturally irrelevant combinations. A cross of fault family × training phase has cells for faults that cannot occur before the link exists. Those cells are noise in the denominator, and removing them makes the remaining percentage mean something.

Configurations this build does not have. A replay-disabled build (19.6 §14) has no reachable retry bins. Ignoring them per build is correct; leaving them uncovered makes every replay-disabled regression report a permanent shortfall that trains the team to ignore the number.

Proven-unreachable scenarios — with the proof, per §51.

And the illegitimate use, which is the common one: ignoring a bin because it is hard to hit. That is §50 row 1 — stimulus missing — relabelled as an exclusion, and the difference is that a missing-stimulus gap has an action while an ignored bin has none.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The record every ignore_bins needs, next to it. A bare ignore_bins with no
// comment is an undocumented reduction of the denominator.
//
//   BIN:      fault_during_training × integrity_fault
//   REASON:   integrity checking is not in the path before the Adapter is
//             operational, so the combination has no architectural meaning.
//   EVIDENCE: 20.1 §51's mode table — the Adapter is not in the path.
//   OWNER:    <name>          REVIEWED: <revision>
ignore_bins no_integrity_before_operational =
  binsof(cp_fault) intersect {F_INTEGRITY} &&
  binsof(cp_phase) intersect {RM_TRAINING};

16. Configuration Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Sampled at a configuration commit. Every value is a CATEGORY
// derived from the capability structure (19.6 §20), never a hardcoded number.
covergroup cg_config;
  // Which negotiation outcomes occurred — including the refusal.
  cp_outcome: coverpoint neg_outcome {
    bins full      = {NEG_FULL};
    bins degraded  = {NEG_DEGRADED};
    bins none      = {NEG_NONE};            // 20.2 §24 — the untested path
  }
 
  // Which protocol enables were active. Categories, not a bitmask product.
  cp_proto_mix: coverpoint proto_mix {
    bins single    = {PM_SINGLE};
    bins dual      = {PM_DUAL};
    bins all       = {PM_ALL};
  }
 
  // Which reliability mode. Conditional on the build (§3, §40).
  cp_reliability_mode: coverpoint rel_mode {
    bins adapter_managed = {RM_ADAPTER};
    bins raw_mode        = {RM_RAW};        // 20.1 §51 — a different check set
    bins no_reliability  = {RM_NONE};
  }
 
  // WAS THE COMMIT ATTEMPTED UNDER TRAFFIC? The bin that validates R8.
  cp_commit_context: coverpoint commit_outstanding_bucket {
    bins idle = {OB_ZERO};
    bins busy = {[OB_ONE:OB_MANY]};        // 19.6 §56's quiesce path
  }
 
  // Did a commit ever land during an operation's lifetime?
  cp_commit_spans_operation: coverpoint commit_spanned_operation {
    bins no  = {0};
    bins yes = {1};                        // 20.3 §37's effect property
  }
 
  // Targeted crosses only — each justified in §31's terms.
  x_outcome_x_reliability: cross cp_outcome, cp_reliability_mode;
  x_commit_context_x_outcome: cross cp_commit_context, cp_outcome;
endgroup

Four notes.

cp_outcome.none is the refusal bin. A link that has never been asked to refuse an incompatible capability set has an untested failure path — and it is the path that first executes against a peer nobody tested with (20.2 §24).

cp_commit_context.busy is what validates R8. A configuration commit on an idle link never exercises quiescence, so 19.6 §23's partial-commit failure mode is untested however many commits occurred.

cp_commit_spans_operation is a derived coverpoint, computed by the join from an obligation's accept and terminal epochs (20.4 §57). It is not observable at any single boundary, which is why the collector needs the models rather than only the monitors.

And x_outcome_x_reliability earns its place by §31's test: if negotiation and reliability mode interact incorrectly, a Raw Mode link negotiates a degraded configuration and then applies Adapter-managed reliability checks that do not exist in that path. That is a concrete bug, so the cross is justified.

17. Width Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Width CLASSES, derived from the capability structure.
function automatic width_class_e classify_width(int unsigned w);
  if (w == capability.max_lanes)                  return WC_MAX;
  if (w == capability.min_supported_width)        return WC_MIN;
  if (w == capability.nominal_width)              return WC_NOMINAL;
  if (w <  capability.nominal_width)              return WC_DEGRADED;
  return WC_INTERMEDIATE;
endfunction
 
covergroup cg_width;
  // Configured width class at each commit.
  cp_configured: coverpoint configured_class {
    bins classes[] = {WC_MIN, WC_DEGRADED, WC_INTERMEDIATE, WC_NOMINAL, WC_MAX};
  }
  // ACTIVE width class — which may differ after repair (19.6 §25's chain).
  cp_active: coverpoint active_class {
    bins classes[] = {WC_MIN, WC_DEGRADED, WC_INTERMEDIATE, WC_NOMINAL, WC_MAX};
  }
  // Was the active width ever below the configured one?
  cp_degraded_operation: coverpoint active_below_configured {
    bins no  = {0};
    bins yes = {1};
  }
endgroup

Two notes.

Configured and active are separate coverpoints because they are separate quantities with separate owners (19.6 §25): configuration is software-owned, the active mask is hardware-owned. A single width coverpoint cannot record that they differed, which is the situation degradation creates.

And WC_INTERMEDIATE exists because degraded widths need not be powers of two. 20.2 §57's trace reached an intermediate width after a repair, and a bin structure that assumes powers of two cannot record it — which then hides a striping bug that only appears at non-power-of-two widths.

18. The Width-Before-and-After Cross

The most valuable cross in the configuration model, and it is a cross the plan must construct deliberately because neither side is observable at one event.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Sampled at RECOVERY EXIT, with the pre-recovery class captured
// at recovery ENTRY. A cross between two moments, not two signals.
covergroup cg_width_recovery;
  cp_before: coverpoint class_before_recovery {
    bins classes[] = {WC_MIN, WC_DEGRADED, WC_INTERMEDIATE, WC_NOMINAL, WC_MAX};
  }
  cp_after: coverpoint class_after_recovery {
    bins classes[] = {WC_MIN, WC_DEGRADED, WC_INTERMEDIATE, WC_NOMINAL, WC_MAX};
    bins failed    = {WC_NONE};              // the recovery did not restore a link
  }
  cp_direction: coverpoint width_direction {
    bins unchanged = {WD_SAME};
    bins narrower  = {WD_NARROWER};          // 20.2 §30 — legal
    bins wider     = {WD_WIDER};             // legal only if the architecture says so
  }
 
  // THE CROSS. Justified in §31's terms below.
  x_before_after: cross cp_before, cp_after;
endgroup

Why this cross is justified — the §31 test, answered concretely.

If the width before a recovery and the width after it interact incorrectly, what bug appears?

Three specific bugs, each in a different cell of the cross:

NOMINAL → DEGRADED exercises 20.2 §28's checker failure — a plan expecting the same width back reports a false failure and gets the whole degradation test deleted.

DEGRADED → NOMINAL exercises whether headroom, watermarks and credit capacity derived from the narrow width are correctly recomputed when the link widens. A design that computes them once at first bring-up is wrong here and correct everywhere else.

MAX → MAX with an intervening lane event exercises the case where degradation was attempted and recovered fully — and it is the cell that distinguishes "no lane ever failed" from "a lane failed and repair restored full width", which look identical in a single-width coverpoint.

And the diagonal is not the interesting part. A cross whose only hit cells are on the diagonal has recorded that nothing ever changed — which is a finding, and it is the finding §50 row 1 exists for.

19. Protocol-Class Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Sampled at object acceptance. NUM_CLASSES is a parameter (§40).
covergroup cg_traffic_class;
  cp_class: coverpoint accepted_class {
    bins c[] = {[0:NUM_CLASSES-1]};
  }
  // Were several classes active in the same window? The Arb/Mux question.
  cp_concurrency: coverpoint active_class_count {
    bins one     = {1};
    bins two     = {2};
    bins several = {[3:NUM_CLASSES]};
  }
  // Was a class ever admitted while another was exhausted? 19.5 §9's overflow.
  cp_asymmetric_exhaustion: coverpoint asymmetric_state {
    bins none              = {AS_NONE};
    bins one_exhausted     = {AS_ONE_ZERO};      // the case a scalar pool breaks
    bins all_exhausted     = {AS_ALL_ZERO};
  }
  // Was a class ever starved of service? R11's coverage item.
  cp_starvation_window: coverpoint max_service_gap_bucket {
    bins short  = {SG_SHORT};
    bins medium = {SG_MEDIUM};
    bins long   = {SG_LONG};                     // 19.5 §49 / 20.3 §45
  }
endgroup

Three notes.

cp_asymmetric_exhaustion.one_exhausted is the bin that exercises 19.5 §9's failure. A global credit pool overflows one class while stalling another, and the bug needs two classes with asymmetric depths and asymmetric load — which is the steady state in production and almost never a directed test.

cp_starvation_window buckets a gap, not a count. 20.3 §45's bounded-wait property is what checks it; this bin records whether a long gap was ever observed, which is what makes the property non-vacuous.

And no protocol mapping is claimed. UCIe maps PCIe and CXL natively (§3); which resource classes exist, how many, and how they map to those protocols is not asserted here. NUM_CLASSES is a parameter and the bins are indices.

20. Traffic-Direction Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. 19.1 §41 established that TX and RX are independent. This is
// the coverage that proves the independence was exercised.
covergroup cg_direction;
  cp_activity: coverpoint direction_activity {
    bins tx_only     = {DA_TX};
    bins rx_only     = {DA_RX};
    bins full_duplex = {DA_BOTH};                  // the operating point
  }
  // One direction stalled while the other progresses — 19.1 §42's shared-ready.
  cp_asymmetric: coverpoint asymmetric_progress {
    bins none         = {AP_NONE};
    bins tx_stalled   = {AP_TX_STALLED};
    bins rx_stalled   = {AP_RX_STALLED};
  }
  x_activity_x_asymmetric: cross cp_activity, cp_asymmetric;
endgroup

Why cp_asymmetric is the coverpoint that matters. 19.1 §42's failure is a shared ready signal, so a stall in one direction stalls both. The bug is invisible under tx_only or rx_only load and invisible under symmetric full-duplex load — it needs one direction stalled while the other has work, which is exactly the AP_TX_STALLED cell crossed with DA_BOTH.

And the cross has one cell that must be non-empty: full_duplex × tx_stalled. If it is empty, the direction-independence property has never been exercised, whatever the total duplex coverage says.

21. Resource Coverage — Occupancy Buckets

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Buckets, not values. Sampled on a push or a pop — the events
// that change occupancy (§12). Parameter-safe (§40).
function automatic occ_bucket_e bucket(int unsigned occ, int unsigned depth);
  if (occ == 0)             return OCC_EMPTY;
  if (occ == 1)             return OCC_ONE;          // §22's boundary
  if (occ <  depth/4)       return OCC_LOW;
  if (occ <  HIGH_WATER)    return OCC_MID;
  if (occ <  depth - 1)     return OCC_HIGH;
  if (occ == depth - 1)     return OCC_NEAR_FULL;    // §22's boundary
  return OCC_FULL;
endfunction
 
covergroup cg_occupancy;
  cp_bucket: coverpoint occ_bucket {
    bins empty     = {OCC_EMPTY};
    bins one       = {OCC_ONE};
    bins low       = {OCC_LOW};
    bins mid       = {OCC_MID};
    bins high      = {OCC_HIGH};
    bins near_full = {OCC_NEAR_FULL};
    bins full      = {OCC_FULL};
  }
  // Threshold crossings — the watermark question (19.4 §29).
  cp_watermark: coverpoint watermark_event {
    bins asserted  = {WM_ASSERT};
    bins deasserted = {WM_DEASSERT};
    bins chatter   = {WM_CHATTER};                  // 19.4 §32 — hysteresis absent
  }
  // Simultaneous push and pop — a per-cycle question (§12's exception).
  cp_simultaneous: coverpoint push_pop {
    bins neither = {2'b00}; bins push_only = {2'b10};
    bins pop_only = {2'b01}; bins both = {2'b11};   // 19.4 §9's drift case
  }
endgroup

Three notes.

Seven buckets, of which four are boundaries. empty, one, near_full and full are single-value bins; the other three are ranges. That asymmetry is deliberate — §22.

cp_watermark.chatter requires a derived event. It is set when the watermark toggles more than once within a short window, which is 19.4 §32's missing-hysteresis signature. No single sample can see it; the collector needs a small amount of state.

And cp_simultaneous.both should be one of the most-hit bins, not one of the rarest. 19.5 §13: a simultaneous push and pop requires both producer and consumer active, which is the intended operating point. If it is rare, the stimulus is not loading the design and every simultaneity bug is unverified.

22. Boundary Bins

Engineering defects concentrate at boundaries, and a bucket structure that spans them hides exactly the values that matter.

BoundaryWhyThe bug it exposes
0the empty/idle caseunderflow; a pop from empty
1the single-entry casea degenerate pointer, $clog2(1) = 0 (19.6 §46)
N−1one below capacityan off-by-one in a full check
Nat capacityoverflow; the truncated counter of 19.5 §11
wrappointer wrapa wrap that assumes a power of two (19.4 §13)
threshold − 1, threshold, threshold + 1the watermark edgea late watermark with insufficient headroom (19.4 §29)

Two rules.

Every boundary gets its own single-value bin, not a range that contains it. A bin [depth-4:depth] is hit by a value of depth-4 and reports the capacity boundary as covered.

And the boundaries are computed from parameters, never written as literals. A bin written as {16} is the capacity boundary of one build and an arbitrary mid-range value of the next — which is 19.6 §45's literal-width failure appearing in the coverage model.

23. Credit Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. The credit model of 19.5, covered. Sampled on credit events.
covergroup cg_credit;
  // Boundaries first (§22).
  cp_value: coverpoint credit_bucket {
    bins zero     = {CB_ZERO};                 // exhaustion
    bins one      = {CB_ONE};
    bins mid      = {CB_MID};
    bins at_cap   = {CB_AT_CAPACITY};          // 19.5 §11's truncation boundary
  }
  // The four update combinations — a per-cycle question (§12's exception).
  cp_update: coverpoint consume_return {
    bins idle        = {2'b00};
    bins return_only = {2'b01};
    bins consume_only = {2'b10};
    bins both        = {2'b11};                // 19.5 §13
  }
  // Multi-unit deltas and their sign — 19.5 §14.
  cp_delta_sign: coverpoint delta_sign {
    bins negative = {DS_NEG};  bins zero = {DS_ZERO};  bins positive = {DS_POS};
  }
  // Epoch events — 19.5 §28/§29.
  cp_epoch_event: coverpoint credit_epoch_event {
    bins fresh_return  = {CE_FRESH};
    bins stale_return  = {CE_STALE};           // the guard, exercised (§49)
    bins rebaseline    = {CE_REBASELINE};
  }
  // Batching — 19.5 §42's deadlock needs the PARTIAL flush.
  cp_flush_reason: coverpoint flush_reason {
    bins threshold = {FR_THRESHOLD};
    bins timer     = {FR_TIMER};               // the liveness mechanism
    bins starved   = {FR_STARVED};
  }
  cp_partial_batch: coverpoint pending_at_flush {
    bins partial = {[1:BATCH_THRESHOLD-1]};    // 19.5 §42 — the critical bin
    bins full    = {[BATCH_THRESHOLD:$]};
  }
  // Was the progress reserve ever engaged? 19.5 §45.
  cp_progress_reserve: coverpoint reserve_engaged { bins yes = {1}; bins no = {0}; }
 
  // Targeted crosses.
  x_zero_credit_x_occupancy: cross cp_value, cp_rx_occupancy_bucket;
  x_stale_x_phase:           cross cp_epoch_event, cp_link_phase;
endgroup

Three notes on which bins matter most.

cp_partial_batch.partial is 19.5 §42's deadlock detector. If it is empty, the flush timer never fired, so the liveness mechanism that prevents a permanent stall with free capacity at both ends is untested. It requires a burst followed by silence, which random stimulus almost never produces.

x_zero_credit_x_occupancy is a justified cross by §31's test: zero credit with the receiver below capacity is the stranded-capacity signature (19.5 §63), and it is a completely different condition from zero credit with the receiver full. Neither coverpoint alone distinguishes them.

And cp_epoch_event.stale_return records the guard working. It is not an error — a stale return during a recovery is expected — but a run in which it never occurs has not exercised the guard, and the crossed version distinguishes "stale during recovery" (normal) from "stale during steady state" (alarming).

24. Fault-Family Coverage

Cover the fault family, not the fault count. "Twelve errors were injected" is not a coverage result; "each of six fault families was injected at least once, in three lifecycle moments" is.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Fault FAMILIES are the environment's own taxonomy (§3) — no
// UCIe error class, code or name is claimed. Sampled at INJECTION and,
// separately, at DETECTION, because the pair is the interesting thing.
typedef enum {
  F_INTEGRITY,        // corruption after integrity was computed (20.2 §43 pt 3)
  F_DROP,             // an object destroyed in flight
  F_DELAY,            // an object or response delayed beyond expectation
  F_FEEDBACK_LOST,    // an acknowledgement or credit return dropped
  F_FEEDBACK_DUP,     // a feedback message duplicated (19.5 §24)
  F_LANE_HEALTH,      // a lane becomes unusable
  F_TRAINING_STALL,   // training progress withheld
  F_PEER_SILENT       // the far end stops responding
} fault_family_e;
 
covergroup cg_reliability;
  cp_injected: coverpoint injected_family {
    bins families[] = {F_INTEGRITY, F_DROP, F_DELAY, F_FEEDBACK_LOST,
                       F_FEEDBACK_DUP, F_LANE_HEALTH, F_TRAINING_STALL,
                       F_PEER_SILENT};
  }
  // Was the expected detector the one that fired? 20.2 §43's injection record.
  cp_detection: coverpoint detection_outcome {
    bins expected_detector = {DO_EXPECTED};
    bins other_detector    = {DO_UNEXPECTED};   // a finding, not a coverage goal
    bins undetected        = {DO_NONE};         // a finding
  }
  // Attempts per object — the retry distribution (20.4 §51).
  cp_attempts: coverpoint attempts_per_object {
    bins one     = {1};
    bins two     = {2};
    bins several = {[3:MAX_ATTEMPTS-1]};
    bins at_budget = {MAX_ATTEMPTS};            // §22's boundary
  }
  // Arrivals versus attempts — this ratio says WHICH fault was injected.
  cp_arrival_ratio: coverpoint arrival_vs_attempt {
    bins equal      = {AR_EQUAL};               // corruption: it arrived, badly
    bins one_fewer  = {AR_ONE_FEWER};           // a drop: it never arrived
    bins surplus    = {AR_SURPLUS};             // a duplicate transmission
  }
  x_family_x_attempts: cross cp_injected, cp_attempts;
endgroup

Three notes.

cp_detection.other_detector and .undetected are findings, not goals. A fault detected by a mechanism other than the expected one means the injection record's expectation was wrong or the detection responsibility is misplaced; an undetected fault means the detection mechanism does not work. Both belong in the coverage report because 20.2 §43's injection record names the expected detector, and comparing expectation against outcome is a coverage question with an error-shaped answer.

cp_arrival_ratio is the discriminator that turns fault injection into evidence. 20.4 §51: equal counts mean the object arrived and failed integrity; one fewer means it was destroyed in flight; a surplus means it was duplicated. A plan that covers only "a fault was injected" cannot confirm the injection did what it intended.

And F_FEEDBACK_LOST / F_FEEDBACK_DUP are the two families nobody injects. 19.5 §24's duplicate return and 18.4 §20's lost return both live here, and both produce a link that slowly stops with every safety property passing.

25. Error-Timing Coverage

The same fault at a different moment is a different test. This cross is where timing-dependent bugs live, and it is the cross most plans omit.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. WHEN the fault landed, as a category rather than a cycle.
typedef enum {
  T_DURING_TRAINING,       // before the link exists
  T_FIRST_OBJECT,          // the very first object — an initialisation corner
  T_STEADY_STATE,          // the easy case
  T_AT_MAX_OCCUPANCY,      // structures full (19.4 §29's headroom)
  T_BEFORE_RESOLUTION,     // after transmit, before the verdict (20.2 §36)
  T_DURING_RECOVERY,       // a second fault while recovering
  T_DURING_CFG_COMMIT      // 19.6 §23's window
} fault_moment_e;
 
covergroup cg_fault_timing;
  cp_moment: coverpoint fault_moment {
    bins moments[] = {T_DURING_TRAINING, T_FIRST_OBJECT, T_STEADY_STATE,
                      T_AT_MAX_OCCUPANCY, T_BEFORE_RESOLUTION,
                      T_DURING_RECOVERY, T_DURING_CFG_COMMIT};
  }
  // THE CROSS. Justified below in §31's terms.
  x_family_x_moment: cross cp_injected_family, cp_moment {
    // Some cells have no architectural meaning (§15).
    ignore_bins integrity_before_link =
      binsof(cp_injected_family) intersect {F_INTEGRITY} &&
      binsof(cp_moment) intersect {T_DURING_TRAINING};
  }
endgroup

Why the cross is justified. The §31 question, answered concretely for three cells:

F_INTEGRITY × T_BEFORE_RESOLUTION is 20.2 §36's scenario — a fault after transmit and before the verdict, which is the window in which reliability state must survive a recovery.

F_LANE_HEALTH × T_DURING_CFG_COMMIT is a fault landing inside the quiesce-and-commit window. If the commit machine does not handle a recovery request during CFG_QUIESCE, it hangs there forever (19.6 §22's missing timeout).

F_FEEDBACK_LOST × T_DURING_RECOVERY is a credit return dropped while the agreement is being re-established — which interacts with the epoch guard and is the only way to reach 19.5 §29's straggler window deliberately.

And three of the seven moments cannot be hit by random injection. T_FIRST_OBJECT, T_AT_MAX_OCCUPANCY and T_DURING_CFG_COMMIT all require the injector to synchronise on an observed condition rather than fire on a timer — which is a fault-sequence requirement, not a coverage one, and §55 is how the coverage gap produces it.

26. Recovery-Context Coverage

The centerpiece. Almost every plan has a bin for "a recovery occurred", and almost none has the bins that make recovery verification meaningful.

The reason is 20.2 §21's sentence: a link returning to operational after a recovery is not a link that has just been brought up. It has obligations, a void resource agreement, possibly a different configuration, and a first-fault record. Each of those is a separate coverage dimension.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. The recovery collector. Context is captured at ENTRY; outcome
// at EXIT; and the cross between them is the model's most valuable item.
covergroup cg_recovery_context;
  // --- CONTEXT, sampled at recovery ENTRY ---
 
  // How much semantic work was live? THE bin that validates R5.
  cp_outstanding: coverpoint outstanding_bucket_at_entry {
    bins none    = {OB_ZERO};                  // the easy case, and the default
    bins one     = {OB_ONE};
    bins few     = {OB_FEW};
    bins many    = {OB_MANY};                  // 20.3 §38's per-tag property
  }
  // Was any transport object unresolved? 20.4 §52's trace.
  cp_unresolved_objects: coverpoint unresolved_bucket_at_entry {
    bins none = {UB_ZERO}; bins some = {UB_SOME};
  }
  // Was credit exhausted? 20.2 §36 — the recovery that starts with no credit.
  cp_credit_at_entry: coverpoint credit_bucket_at_entry {
    bins zero    = {CB_ZERO};                  // the interesting one
    bins partial = {CB_PARTIAL};
    bins full    = {CB_AT_CAPACITY};
  }
  // Was a response already in flight? The lost-response ambiguity (20.2 §37).
  cp_response_in_flight: coverpoint response_in_flight_at_entry {
    bins no = {0}; bins yes = {1};
  }
  // Was a configuration change pending? 19.6 §23's window.
  cp_cfg_pending: coverpoint cfg_change_pending_at_entry {
    bins no = {0}; bins yes = {1};
  }
  // Was traffic bidirectional? 19.1 §41.
  cp_bidirectional: coverpoint both_directions_active_at_entry {
    bins no = {0}; bins yes = {1};
  }
  // Was the first-fault record ALREADY valid — a second fault? 19.1 §35.
  cp_first_fault_already_set: coverpoint first_fault_valid_at_entry {
    bins no = {0}; bins yes = {1};
  }
 
  // --- OUTCOME, sampled at recovery EXIT ---
  cp_outcome: coverpoint recovery_outcome {
    bins restored_same     = {RO_SAME};
    bins restored_degraded = {RO_DEGRADED};    // 20.2 §30
    bins failed            = {RO_FAILED};      // 20.2 §42
  }
  cp_duration: coverpoint recovery_duration_bucket {
    bins fast   = {RD_FAST};
    bins normal = {RD_NORMAL};
    bins slow   = {RD_SLOW};                   // near the permitted budget
  }
 
  // --- THE CROSSES (§31) ---
  x_outstanding_x_outcome: cross cp_outstanding, cp_outcome;
  x_credit_x_outstanding:  cross cp_credit_at_entry, cp_outstanding;
  x_cfg_x_outstanding:     cross cp_cfg_pending, cp_outstanding;
endgroup

Four readings.

cp_outstanding.none is the default and the useless one. A regression in which every recovery happened with nothing outstanding exercised the recovery path and never tested preservation — which is 20.3 §38's entire property set and 20.4 §31's entire reset-policy argument. If few and many are empty, R5 is unverified however many recoveries occurred.

cp_first_fault_already_set.yes is the second-fault case. 19.1 §35's sticky-first record can only be shown to be sticky-first if a second fault arrives while the first is recorded — so this bin is what makes 20.3 §38's stickiness property non-vacuous.

x_credit_x_outstanding with zero × many is 20.2 §36's flagship scenario and §46's walkthrough: a recovery entered with obligations live and no credit available, so the agreement must be re-established before any of them can progress.

And cp_duration.slow requires a fault that persists. A run whose recoveries all complete quickly has never exercised the recovery-bound liveness property (20.2 §47) anywhere near its limit.

27. The Recovery-Outcome Cross

x_outstanding_x_outcome is worth its own section because its cells have completely different meanings and one of them is a trap.

CellWhat it exercises
none × restored_samethe trivial recovery — every regression has thousands
many × restored_samepreservation with full restoration — R5's main case
many × restored_degradedpreservation and reconfiguration together20.2 §30
many × failedclassification of live obligations on a fatal end — R12, 20.2 §42
none × failedfatal handling with nothing at stake — the easy fatal case
one × restored_degradedthe minimal reconfiguration-with-work case

Two properties of this cross.

many × failed is the cell that exposes the worst bug class in the module. Obligations live at a fatal end must each receive a classification — failed, cancelled, unknown or reported (20.2 §41) — and a design that reports success, or silently drops them, is only caught in this cell. It requires a persistent fault injected while work is outstanding, which no random regression produces.

And the trap is that the cross closes easily on the diagonal. none × restored_same fills within minutes and dominates the hit counts. Reading the cross's percentage rather than its cells reports it as nearly closed while the four cells that matter are empty — which is why §58's review table lists what an uncovered cell means rather than only whether it is covered.

28. Semantic, Object and Attempt Coverage

20.1 §22's three-level identity hierarchy, covered. These bins test the verification architecture as much as the design.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. The multiplicity structure of each completed operation,
// computed by the join (20.4 §20).
covergroup cg_identity_hierarchy;
  cp_objects_per_operation: coverpoint objects_per_op {
    bins one   = {1};
    bins two   = {2};                          // fragmentation
    bins many  = {[3:$]};
  }
  cp_attempts_per_object: coverpoint attempts_per_obj {
    bins one     = {1};
    bins two     = {2};                        // one retry
    bins several = {[3:$]};
  }
  cp_deliveries: coverpoint deliveries_per_op {
    bins zero_with_failure = {0};              // 20.3 §33's third value
    bins exactly_one       = {1};
    illegal_bins duplicate = {[2:$]};          // §40 — and there is an assertion
  }
  // Did objects of one operation ever complete out of order?
  cp_object_order: coverpoint object_completion_order {
    bins in_order  = {OO_ORDERED};
    bins reordered = {OO_REORDERED};           // legal; must be exercised
  }
 
  // The multiplicity cross — the four combinations that matter.
  x_objects_x_attempts: cross cp_objects_per_operation, cp_attempts_per_object;
endgroup

Four cells of the cross, and each is a distinct verification situation:

CellSituation
1 object × 1 attemptthe clean path (20.4 §49)
1 object × 2+ attemptsretry without fragmentation (20.4 §51)
2+ objects × 1 attempt eachfragmentation without retry — the parts bitmap (20.4 §10)
2+ objects × 2+ attemptsboth at once — one part retried while others succeed

The last cell is the one that breaks weak environments. A parts bitmap combined with per-object attempt tracking, where one part is retried and another is not — and an environment that stores attempts per operation rather than per object cannot represent it at all, which is why 20.4 §18 keys the transport record per object.

And cp_deliveries uses illegal_bins legitimately by §13's test: the basis is an architectural contract — exactly-once semantic delivery — and there is also an assertion (20.3 §33), which is the primary checker.

29. Ordering Coverage

Do not cover "ordered versus unordered". Cover the four situations that ordering verification actually needs.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. No UCIe ordering rule is claimed (§3). These bins describe
// the ORDERING-GROUP abstraction of 20.3 §22.
covergroup cg_ordering;
  // A legal reorder was OBSERVED — required for 20.3 §22's second row.
  cp_legal_reorder: coverpoint legal_reorder_observed {
    bins no = {0};
    bins yes = {1};                            // if empty, one order untested
  }
  // A required dependency was PRESENT and preserved.
  cp_dependency: coverpoint dependency_case {
    bins none_required   = {DC_NONE};
    bins required_and_ok = {DC_HONOURED};      // the property fired non-vacuously
  }
  // Independent responses swapped — the aliasing precondition.
  cp_swap: coverpoint independent_swap_observed { bins no = {0}; bins yes = {1}; }
  // A stale identity or generation was rejected — 19.2 §21's guard, exercised.
  cp_stale_identity: coverpoint stale_identity_event {
    bins none     = {SI_NONE};
    bins rejected = {SI_REJECTED};             // §49's scenario
  }
  // The immediate-reuse window — 20.3 §67's cover, promoted to a bin.
  cp_reuse_gap: coverpoint retire_to_reuse_gap {
    bins immediate = {[0:1]};                  // the aliasing window
    bins short     = {[2:15]};
    bins long      = {[16:$]};
  }
  x_reuse_x_reorder: cross cp_reuse_gap, cp_legal_reorder;
endgroup

Two notes.

cp_dependency.required_and_ok is what makes the precedence property non-vacuous. 20.3 §22's property only fires when a pair with a required precedence exists. If the stimulus never generates such a pair, the property passes vacuously and the ordering rule is unverified.

And x_reuse_x_reorder with immediate × yes is the aliasing cell. A delayed response for a retired operation meeting its identity's new owner requires both an immediate reuse and a reorder — 19.2 §21's exact precondition. Neither coverpoint alone reaches it, and it is the cross that justifies itself most cleanly under §31.

30. Concurrency Coverage

Combinations that catch race bugs, and they are combinations of events, not of configuration values.

CombinationThe bug it exposes
retry + full-duplex traffica shared ready or a shared retry resource across directions (19.1 §42)
recovery + outstanding obligationsR5 — the preservation failure (20.4 §31)
credit consume + return, same cyclethe drift bug (19.5 §13)
configuration commit + active trafficthe partial commit (19.6 §23)
fault + first-fault already validlast-capture instead of first-capture (19.1 §35)
allocation + retirement, same index19.3 §24's simultaneous ring event
push + pop at capacity boundarythe occupancy drift at the worst value (19.4 §9)
watermark assert + producer already committedinsufficient headroom (19.4 §29)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Concurrency as an explicit enumeration — because a cross of
// the underlying dimensions would produce mostly meaningless cells (§32).
typedef enum {
  CC_NONE,
  CC_RETRY_DUPLEX,
  CC_RECOVERY_WITH_WORK,
  CC_CREDIT_SIMULTANEOUS,
  CC_COMMIT_UNDER_TRAFFIC,
  CC_SECOND_FAULT,
  CC_ALLOC_RETIRE_SAME,
  CC_PUSH_POP_AT_CAPACITY,
  CC_WATERMARK_LATE
} concurrency_case_e;
 
covergroup cg_concurrency;
  cp_case: coverpoint concurrency_case {
    bins cases[] = {CC_RETRY_DUPLEX, CC_RECOVERY_WITH_WORK,
                    CC_CREDIT_SIMULTANEOUS, CC_COMMIT_UNDER_TRAFFIC,
                    CC_SECOND_FAULT, CC_ALLOC_RETIRE_SAME,
                    CC_PUSH_POP_AT_CAPACITY, CC_WATERMARK_LATE};
  }
endgroup

Why this is an enumeration rather than a cross. Each case is a named, hand-chosen combination whose value the plan can defend. A cross of "is there a retry" × "is it duplex" × "is a commit pending" × "is credit zero" × ... produces sixteen or more cells of which eight are these and the rest are noise — §32's failure, and §33's control.

And the enumeration is the coverage counterpart of 20.2 §44's stage-3 fault combinations. Both are hand-chosen interaction hypotheses; the fault plan generates them and this coverage records that they occurred.

31. The Cross-Design Rule

One question decides whether two dimensions should be crossed:

"If these two dimensions interact incorrectly, what specific bug appears?"

If the answer is a concrete, nameable failure, cross them. If the answer is "I'm not sure, but it might be interesting", do not.

Worked, on four candidate crosses:

CandidateAnswerVerdict
width before × width after recoveryheadroom and credit capacity derived from the old width are not recomputed (§18)cross
credit-zero × receiver occupancyzero credit with a non-full receiver is stranded capacity, a different condition entirely (§23)cross
outstanding-at-recovery × recovery outcomeobligations at a fatal end must be classified, not dropped (§27)cross
identity-reuse gap × reorder observeda delayed response meets the identity's new owner (§29)cross
protocol class × fault family(none — a fault does not care which class the object carried)do not cross
width class × occupancy bucket(none — occupancy is in entries, not lanes)do not cross
lifecycle phase × occupancy bucket(mostly meaningless — structures are empty before operation)do not cross

Two consequences.

The justification belongs in the code, next to the cross. A cross with no comment naming its bug is a cross nobody can review or delete — and an unreviewable cross is what §32's model is made of.

And "do not cross" is not the same as "do not cover". Protocol class and fault family both deserve their own coverpoints; what they do not deserve is a cell for every pairing, because the pairing has no distinct failure mode and its cells are indistinguishable from one another in what they prove.

32. Wrong Cross — the Cartesian Product

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — every dimension crossed with every other.
covergroup cg_everything;
  cp_width:    coverpoint width_class    { bins w[] = {[0:5]};  }   //  6
  cp_rate:     coverpoint rate_class     { bins r[] = {[0:3]};  }   //  4
  cp_proto:    coverpoint proto_mix      { bins p[] = {[0:7]};  }   //  8
  cp_class:    coverpoint traffic_class  { bins c[] = {[0:7]};  }   //  8
  cp_fault:    coverpoint fault_family   { bins f[] = {[0:7]};  }   //  8
  cp_phase:    coverpoint link_phase     { bins s[] = {[0:4]};  }   //  5
  cp_occ:      coverpoint occ_bucket     { bins o[] = {[0:6]};  }   //  7
  cp_credit:   coverpoint credit_bucket  { bins k[] = {[0:3]};  }   //  4
 
  x_all: cross cp_width, cp_rate, cp_proto, cp_class,
               cp_fault, cp_phase, cp_occ, cp_credit;
endgroup

6 × 4 × 8 × 8 × 8 × 5 × 7 × 4 = 1,720,320 bins.

Five properties, and the fifth is why this is worse than useless.

Most cells are impossible. A fault family crossed with a training phase where that fault cannot occur; an occupancy bucket crossed with a phase in which structures are empty; a credit bucket crossed with a phase in which no agreement exists. The impossible fraction is the large majority.

The rest are mostly indistinguishable. Width class 3 with rate class 2 and protocol mix 5 proves nothing that width class 3 with rate class 2 and protocol mix 4 does not.

Closure becomes bin-count management. The percentage is driven by how many cells the stimulus happens to sweep, so the fastest way to raise it is to randomise more dimensions — which is §56's failure, and it improves the number while improving nothing else.

Diagnostic meaning is gone. An uncovered cell in a million-cell cross cannot be classified by §50, because nobody can say what situation it represents or whether it matters. The classification step, which is the actual value of coverage, becomes impossible.

And it displaces the model that would have worked. Effort spent driving a million meaningless cells is effort not spent on §26's twelve recovery-context bins — and those twelve are where R5 and R12 are verified. The Cartesian model is not merely inefficient; it consumes the budget that the useful model needed.

The corrected architecture is the six collectors of §8 with the twelve targeted crosses this chapter has justified individually — of the order of a few thousand meaningful bins rather than 1.7 million, and every one of them answers a question somebody wrote down.

33. Controlling Cross Growth

Four techniques, in the order to reach for them.

Cross only justified pairs (§31). This is the primary control and the others are secondary.

Bucket before crossing. Crossing occupancy buckets rather than occupancy values turns a depth-256 dimension into seven bins (§21). The buckets must include the boundaries (§22) or the reduction throws away the interesting values.

Use with and binsof filters to remove architecturally meaningless cells rather than leaving them uncovered forever:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Filter at the cross, with a documented reason (§15).
x_fault_x_phase: cross cp_fault, cp_phase {
  // Structures are empty before operation, so occupancy-dependent faults have
  // no meaning there.
  ignore_bins pre_operational =
    binsof(cp_phase) intersect {RM_RESET, RM_TRAINING} &&
    binsof(cp_fault) intersect {F_INTEGRITY, F_FEEDBACK_LOST, F_FEEDBACK_DUP};
}

And partition into separate covergroups rather than one wide cross. Two three-way crosses in two collectors are far more interpretable than one six-way cross, and each closes independently — so a gap names one collector rather than a coordinate in a hypercube.

34. Assertions and Coverage Are a Pair

Every meaningful requirement needs both, and they answer opposite halves of the same question.

AssertionCover
Statesthe behaviour was correctthe situation occurred
Failsat the illegal cyclenever
Silent whenthe situation never arosethe situation arose and was wrong
Togethercorrect, and exercised

Worked, on R3 — exactly-once delivery:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The ASSERTION: no semantic duplicate, ever. Safety (20.3 §33).
property p_delivered_at_most_once(int unsigned tag);
  @(posedge clk) disable iff (!por_n)
    (sem_deliver_fire && (sem_deliver_tag == tag))
      |=> always !(sem_deliver_fire && (sem_deliver_tag == tag));
endproperty
 
// The COVER: a retry actually happened, while an obligation was live. Without
// this, the assertion above is satisfied by a design that never retries.
c_retry_with_live_obligation: cover property (
  @(posedge clk) (attempt_fire && (attempt_num > 1) && chk_outstanding_any));
 
// And the COVERAGE ITEM that quantifies it (§28).
//   cp_attempts_per_object.two and .several must both be non-empty.

Three notes.

The assertion alone is satisfiable by a design that never retries. At most once is trivially true of zero. 20.3 §34's pairing argument, and this is its coverage form.

The cover alone proves nothing about correctness. It says the retry happened; the assertion says the retry did not duplicate.

And the coverage item is a third thing. The cover property is a boolean — did it ever happen. The coverage item is a distribution — how many attempts, in what contexts — which is what tells the plan whether one retry was exercised or the full retry budget.

35. Assertion Activation Coverage

A property that never activates adds no confidence, and its passing row is the most misleading line in a regression report.

Three quantities, reported together (20.3 §49):

QuantityFinds
antecedent covervacuity — the trigger never occurred
pass counta property that was never bound at all
fail countthe defect

Read together: non-zero pass count + covered antecedent + zero failures = genuinely checked. Non-zero pass count + uncovered antecedent = a vacuous property counted as the strongest evidence in the report.

And the quantity that is not coverage: the number of properties. A plan reporting "we have 340 assertions" has reported a code metric. Three hundred and forty properties of which forty are vacuous and twelve are unbound is a weaker plan than eighty properties all demonstrated to activate — and only these three columns distinguish them.

36. Code Coverage — What It Is Evidence Of

Statement, branch, expression, toggle and FSM coverage are evidence about the implementation. They are useful and they are not requirement coverage.

KindAnswersCannot answer
statement / blockwas this line executed?was it executed in the situation that matters?
branchwere both directions taken?were they taken with the right state elsewhere?
expressionwere the sub-conditions independently exercised?is the expression the right expression?
toggledid this signal change value?did it change for an architecturally meaningful reason (§37)
FSM state / arcwere all states and arcs reached?were the arcs reached in legal sequences, from legal contexts?

Two properties.

Code coverage finds dead logic, which functional coverage cannot. A branch nothing ever takes is either unreachable — a design simplification opportunity — or a feature nobody tested. That is a real and distinct finding, and it is why code coverage belongs in the plan.

And 100% code coverage is compatible with a completely unverified architecture. Every line of a recovery handler can execute during recoveries with nothing outstanding; every line of a credit machine can execute without a single simultaneous consume-and-return. 19.5 §42's batching deadlock executes no unusual line — the deadlocked state is reached by ordinary lines in an ordinary order. Code coverage reports it as fully covered.

Code coverage is a floor: below 100% something is unexercised. It is never a ceiling: 100% says nothing about which scenarios occurred.

37. The Toggle-Coverage Trap

Worked, and it is the sharpest illustration of §36.

A configuration register bit — "degraded width supported" — is written once by reset to 0, and once at initialisation to 1.

Toggle coverage: both values seen. 100%. Covered.

What was never tested: a runtime transition of that bit, a configuration commit that changes it, the quiesce path around that commit (19.6 §22), or any traffic under the changed configuration.

Three properties.

The toggle happened for the wrong reason. Reset initialisation is not a configuration change; the architectural situation the bit exists to represent was never entered.

Functional scenario coverage distinguishes them and toggle coverage cannot, because toggle coverage has no concept of why a value changed. §16's cp_commit_context is the bin that separates a commit at initialisation from a commit under traffic.

And the same trap applies to FSM arc coverage. An arc taken once during bring-up is "covered" — so RECOVERY → OPERATIONAL reached once, in a recovery with nothing outstanding, closes the arc while §26's entire context dimension is empty. The arc is a level-5 measurement of a level-2 question.

38. The Collector Architecture

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Collectors consume MONITOR EVENTS and MODEL state, exactly as
// scoreboards do (20.4 §6). They are peers of the models, not part of them.
class ucie_cov_base;
  ucie_semantic_model  sem;      // read-only handles for derived bins
  ucie_transport_model trans;
  ucie_resource_model  res;
  ucie_link_model      link;
  bit                  enabled;  // §8 — each collector switchable
 
  pure virtual function void on_event(ucie_event e);
endclass
 
class ucie_recovery_cov extends ucie_cov_base;
  // Context captured at entry, held until exit — the cross of §27 spans two
  // moments, so the collector needs state.
  occ_bucket_e captured_outstanding;
  credit_bucket_e captured_credit;
  bit captured_response_in_flight;
  longint entry_cycle;
 
  function void on_event(ucie_event e);
    if (!enabled) return;
    case (e.kind)
      EVT_RECOVERY_ENTER: begin
        captured_outstanding       = bucket_ob(sem.outstanding_count());
        captured_credit            = bucket_credit(res.expected_credit(0));
        captured_response_in_flight = trans.any_awaiting_response();
        entry_cycle                = e.cycle;
        cg_recovery_context.sample();          // the CONTEXT half
      end
      EVT_RECOVERY_EXIT: begin
        recovery_outcome        = classify_outcome(link);
        recovery_duration_bucket = bucket_duration(e.cycle - entry_cycle);
        cg_recovery_context.sample();          // the OUTCOME half + crosses
      end
      default: ;
    endcase
  endfunction
endclass

Three notes.

The collector holds state, and that is expected. §27's cross spans recovery entry and exit, so the context must be captured and held. A collector that samples only at exit has lost the context; one that samples only at entry has lost the outcome.

It reads the models, not the design. sem.outstanding_count() is the environment's independently maintained count (20.4 §10) — not a design register, which is §42.

And enabled is per collector (§8). A collector that cannot be switched off individually will be switched off collectively the first time a long run needs the cycles.

39. A Subscriber Collector

The bridge into 20.6. A collector is naturally a subscriber: it consumes an observation stream and holds no opinions about correctness.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. 20.6 owns the environment architecture; this is the shape the
// coverage model imposes on it.
class ucie_recovery_cov_sub extends uvm_subscriber #(ucie_event);
  `uvm_component_utils(ucie_recovery_cov_sub)
 
  ucie_recovery_cov cov;
 
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cov = new();
    // Model handles arrive by configuration, not by hierarchy peeking.
    if (!uvm_config_db #(ucie_model_bundle)::get(this, "", "models", models))
      `uvm_fatal(get_type_name(), "model bundle not configured")
    cov.sem = models.sem; cov.trans = models.trans;
    cov.res = models.res; cov.link  = models.link;
  endfunction
 
  function void write(ucie_event t);
    cov.on_event(t);              // one entry point; no correctness logic here
  endfunction
endclass

Two notes.

write() does one thing. 20.4 §9's monitor discipline applied to subscribers: a subscriber that also checks, logs and formats is a component that cannot be reused or tested alone.

And the model handles arrive by configuration. Not by uvm_top lookup, not by a global, and not by reaching up the hierarchy — because a collector that finds its models by hierarchy path cannot be instantiated twice or moved. 20.6 develops the configuration architecture; the requirement originates here.

40. Parameterised Covergroups

Bins must follow the parameters, or the model is correct for one build.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. Every bound comes from a parameter or the capability structure
// (19.6 §20). No literal bin values anywhere (19.6 §45's rule, in coverage).
covergroup cg_parameterised;
  cp_class: coverpoint traffic_class {
    bins c[] = {[0:NUM_CLASSES-1]};                      // follows the parameter
  }
  cp_occupancy: coverpoint occ_bucket {
    // Buckets computed from DEPTH (§21), not hardcoded ranges.
    bins empty     = {OCC_EMPTY};
    bins one       = {OCC_ONE};
    bins near_full = {OCC_NEAR_FULL};
    bins full      = {OCC_FULL};
    bins mid[3]    = {[OCC_LOW:OCC_HIGH]};
  }
  cp_credit: coverpoint credit_bucket {
    bins zero   = {CB_ZERO};
    bins at_cap = {CB_AT_CAPACITY};                      // = active_capacity
    bins mid[4] = {[CB_ONE:CB_BELOW_CAP]};
  }
endgroup
 
// And the feature-conditional bins (§3, §15): a replay-disabled build has no
// reachable retry bins, so they are IGNORED for that build rather than left
// permanently uncovered.
generate
  if (!REPLAY_ENABLE) begin : g_no_replay_cov
    // The retry coverpoints are not instantiated at all in this build.
  end
endgenerate

Three notes.

bins c[] = {[0:NUM_CLASSES-1]} creates one bin per class automatically. A hardcoded four-bin list is wrong on an eight-class build and reports full class coverage on half the classes19.6 §53's hardcoded-default failure in the coverage model.

Bucket boundaries are symbolic enum values, computed by a function from DEPTH (§21). The covergroup never sees a numeric depth, which is what makes it survive a parameter sweep.

And feature-conditional coverage is generated per build, not left uncovered. §15's second legitimate use: a replay-disabled regression reporting a permanent shortfall trains the team to ignore the number.

41. The Dynamic-Bin Caveat

Covergroup bins are fixed once the covergroup is constructed, and the supported value set may not be known until configuration is read. Three practical approaches, and one to avoid.

Approach 1 — normalise to categories (preferred). Define a fixed enumeration of classes — minimum, nominal, maximum, degraded — and map the runtime value to a class at sample time (§17). The bin structure is static; the mapping is dynamic. This is what every width, occupancy and credit coverpoint in this chapter does.

Approach 2 — one covergroup instance per configuration. Construct the collector after configuration is known, with bins sized from it. Practical where the number of configurations is small.

Approach 3 — a superset with per-build exclusions. Bins for every architecturally supported value, with ignore_bins applied per build from the capability structure (§40). Costs a slightly larger denominator and keeps one model across products.

And the approach to avoid: generating bin expressions from strings or building the covergroup by macro expansion per value. It works, it is unmaintainable, and the resulting model cannot be reviewed — which forfeits the entire point of §58's review table.

42. Coverage-Database Independence

A coverage model built on the design's internal encodings is a coverage model that cannot record that the design did something wrong.

Sample from monitor events and model state. Sample a design-internal signal only when the item is explicitly a white-box implementation-coverage item, and label it.

Three reasons, in increasing order of consequence.

Portability. An internal enum changes on a refactor and the coverage database becomes non-comparable across releases — so the trend line, which is the main use of a coverage database over time, is lost.

Interpretability. A bin named after an internal state does not answer an architectural question. §58's review table has a column for the architectural question, and an internal-state bin cannot fill it.

And correctness of the measurement. 20.2 §9's bug is a design that enters its ACTIVE state having committed an unconfirmed configuration. A coverage model sampling the design's state records that as an operational entry — it agrees with the design that the link is up. The environment's own five-phase model records that the operational phase was never legitimately entered (§11), which is a completely different measurement of the same run.

The legitimate white-box exception, labelled:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WHITE-BOX IMPLEMENTATION COVERAGE. Non-portable by construction (20.3 §54).
// Lives in a separate collector so it can be dropped without touching the
// architectural model.
covergroup cg_impl_wb;
  cp_replay_ptr_wrap: coverpoint replay_ptr_wrapped { bins yes = {1}; }
  cp_freelist_empty:  coverpoint freelist_empty     { bins yes = {1}; }
  cp_fsm_encoding:    coverpoint dut_state_q        { bins s[] = {[0:5]}; }
endgroup

43. Wrong Collector — Sampling the Design's Enum

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG for an architectural coverage item — samples the DUT's state register.
covergroup cg_lifecycle_bad @(cfg_commit_event);
  cp_phase: coverpoint dut.link_state_q {          // the DESIGN's encoding
    bins reset = {RESET}; bins train = {MBTRAIN};
    bins active = {ACTIVE}; bins retrain = {PHYRETRAIN};
  }
endgroup

Four consequences.

It agrees with the design about which phase the link is in, including phases the link should not have been in — 20.2 §9's bug recorded as normal operation.

It cannot represent the environment's distinction between training and recovery. 20.2 §7's was_operational flag: a restart and a recovery have different expectations, and if the design uses one state for both, so does the coverage model — so §26's entire context dimension becomes unattributable.

It breaks on a state-machine refactor, taking the historical coverage database with it (§42).

And it makes the bins unreviewable. A bin named MBTRAIN answers "was the design in its MBTRAIN state?"which is not an architectural question anybody wrote down, and §58's review table has no row for it.

The corrected collector samples the link model's phase (§11), which is derived from observed evidence and belongs to the environment.

44. The Flagship Coverage Model

Six collectors, roughly a few thousand meaningful bins, twelve justified crosses. This is the model §32's Cartesian product displaces.

CollectorCoverpointsJustified crosses
link_covphase entered, arc, training attempts
config_covnegotiation outcome, protocol mix, reliability mode, commit context, commit spans operation, configured width class, active width classoutcome × reliability mode; commit context × outcome
resource_covoccupancy bucket, watermark event, simultaneous push/pop, credit bucket, credit update, delta sign, epoch event, flush reason, partial batch, progress reservecredit-zero × RX occupancy; stale × phase
reliability_covinjected family, detection outcome, attempts per object, arrival ratio, fault momentfamily × attempts; family × moment
recovery_covoutstanding at entry, unresolved objects, credit at entry, response in flight, config pending, bidirectional, first-fault already set, outcome, durationoutstanding × outcome; credit × outstanding; config-pending × outstanding
traffic_covclass, class concurrency, asymmetric exhaustion, starvation window, direction activity, asymmetric progress, objects per operation, attempts per object, deliveries, object order, reorder observed, dependency case, swap observed, stale identity, reuse gap, concurrency casewidth before × after; objects × attempts; reuse gap × reorder; direction × asymmetric

Four properties of this model.

Every cross has a named bug (§31), written next to it in the source.

Six collectors close independently, so a gap names its owner. A recovery_cov gap is a fault-sequence problem; a traffic_cov gap is a stimulus-mix problem — different people, different next actions.

The bins with the highest value are the rarest. recovery_cov's many × failed cell, resource_cov's partial batch, traffic_cov's immediate reuse × reorderall three require deliberate, synchronised stimulus and none will appear by accident. §55 is how the gaps produce that stimulus.

And the model is reviewable. Roughly sixty coverpoints and twelve crosses fit in §58's table, and a reviewer can ask of each row "what does an uncovered result mean?"which is the question a million-cell hypercube makes unanswerable.

45. Wrong Model, Corrected

Side by side, on the same regression.

Cartesian model (§32)Targeted model (§44)
Bins1,720,320~a few thousand
Reachable fractiona small minoritynearly all
Reported coverage after a long random regressione.g. 8%e.g. 74%
What the shortfall meansunknown — cells are uninterpretablea list of named questions
Fastest way to raise the numberrandomise more dimensions (§56)write the missing scenario
Fits in a reviewnoyes (§58)
Finds R5 unverifiednoyes — recovery_cov outstanding bins empty

The row that matters is the last one. In the Cartesian model, the cells covering recovery with many outstanding obligations exist — buried among 1.7 million others, contributing 0.0002% to the percentage, invisible in any report anybody reads. In the targeted model they are four of twelve cells in one cross, and their emptiness is the headline finding.

And the second-to-last row is the honest cost. 74% is a worse-looking number than a Cartesian model tuned by sweeping dimensions. The targeted model is harder to close and its number means something, which is §5's argument stated as a trade.

46. Scenario 1 — Recovery With a Live Request and Zero Credit

20.2 §36's trace, read as coverage. One obligation accepted and attempted, credit exhausted, a fault, a recovery, and a completion afterwards.

CollectorCoverpointBin hit
link_covcp_arcoper_to_recovery, then recovery_to_oper
link_covcp_phase_enteredrecovery, then oper
recovery_covcp_outstandingone
recovery_covcp_credit_at_entryzero
recovery_covcp_unresolved_objectssome
recovery_covcp_response_in_flightno
recovery_covcp_outcomerestored_same
recovery_covx_credit_x_outstandingzero × one
recovery_covx_outstanding_x_outcomeone × restored_same
resource_covcp_epoch_eventrebaseline
resource_covcp_valuezero, then at_cap
reliability_covcp_injectedF_INTEGRITY (or the injected family)
reliability_covcp_momentT_BEFORE_RESOLUTION
reliability_covcp_attemptstwo
reliability_covcp_arrival_ratioone_fewer — the first attempt never arrived
traffic_covcp_attempts_per_objecttwo
traffic_covcp_deliveriesexactly_one
config_covcp_commit_contextbusy — the re-commit happened with work outstanding

Four readings.

Eighteen bins from one scenario, across five collectors. That density is the argument for scenario-driven stimulus: a hand-written scenario hits bins in every collector simultaneously, which random traffic reaches only by coincidence.

x_credit_x_outstanding = zero × one is the cell this scenario exists for. A recovery entered with an obligation live and no credit available means the obligation cannot progress until the agreement is re-established — so it exercises R5, R6 and R7 in one run.

cp_arrival_ratio.one_fewer confirms the injection did what it intended. Two attempts and one arrival means the first was destroyed in flight. Had the ratio been equal, the injected fault was a corruption rather than a drop — a different test, and the bin is what distinguishes them (§24).

And config_cov.cp_commit_context.busy is hit as a side effect. The re-negotiation at recovery exit is a configuration commit, and it happens with one obligation outstanding. A plan that only commits configuration from an idle state would have this bin empty — and R8 unverified.

20.2 §30's trace plus a retry. Three obligations outstanding, a lane lost, recovery to a narrower width, and one object retried afterwards.

CollectorCoverpoint / crossBin hit
recovery_covcp_outstandingfew
recovery_covcp_outcomerestored_degraded
recovery_covx_outstanding_x_outcomefew × restored_degraded
recovery_covcp_bidirectionalyes
config_covcp_outcomedegraded
config_covcp_configured / cp_activenominal / degraded
config_covcp_degraded_operationyes
cg_width_recoverycp_before / cp_afternominal / degraded
cg_width_recoverycp_directionnarrower
cg_width_recoveryx_before_afternominal × degraded
reliability_covcp_injectedF_LANE_HEALTH
reliability_covcp_momentT_STEADY_STATE
reliability_covcp_attemptstwo
traffic_covcp_objects_per_operationone
traffic_covx_objects_x_attempts1 object × 2 attempts
link_covcp_arcoper_to_recovery, recovery_to_oper

Three readings.

x_before_after = nominal × degraded is the cell §18 was built for. It exercises whether headroom, watermarks and credit capacity derived from the wide configuration are recomputed for the narrow one — and a design that computes them once at bring-up is wrong here and correct in every other cell.

few × restored_degraded combines two dimensions that are usually tested separately. Degradation is normally tested from idle; preservation is normally tested with a same-width recovery. The cell where both happen at once is the one where 20.2 §28's false-failure checker gets written, because the width changed and the obligations must be unchanged.

And the retry after the recovery matters. The retransmission happens under the new configuration while the obligation carries the old configuration epoch (20.4 §52) — so cp_commit_spans_operation is also hit, which is R8's coverage item.

48. Scenario 3 — Full Duplex With One Direction Backpressured

CollectorCoverpoint / crossBin hit
traffic_covcp_activityfull_duplex
traffic_covcp_asymmetrictx_stalled
traffic_covx_activity_x_asymmetricfull_duplex × tx_stalled
traffic_covcp_concurrencytwo or several
traffic_covcp_asymmetric_exhaustionone_exhausted
traffic_covcp_starvation_windowmedium or long
resource_covcp_valuezero on the stalled direction's class
resource_covx_zero_credit_x_occupancyzero × below capacity — stranded
resource_covcp_flush_reasontimer if returns were batched
resource_covcp_partial_batchpartial
resource_covcp_simultaneousboth — the operating point

Four readings.

full_duplex × tx_stalled is the cell 19.1 §42's shared-ready bug lives in. Under tx_only load the stall is expected; under symmetric duplex load both directions stall together and it looks correct. Only one direction stalled while the other has work distinguishes a shared ready from independent ones.

x_zero_credit_x_occupancy = zero × below capacity is the stranded-capacity signature (19.5 §63) — and it is a completely different condition from zero credit with a full receiver. The cross is what separates a congested link from a stalled one.

cp_partial_batch.partial is hit here, which is 19.5 §42's deadlock precondition. The scenario reaches it because a backpressured direction produces exactly the burst-then-quiet pattern the partial flush needs.

And cp_asymmetric_exhaustion.one_exhausted exercises 19.5 §9's scalar-pool failure — one class at zero while another has room, which a global credit counter handles wrongly in both directions.

49. Scenario 4 — A Stale Epoch Event After Recovery

19.5 §29's straggler and 19.2 §21's aliasing, in one scenario. A recovery completes; a credit return tagged to the dead agreement arrives; and separately a delayed response arrives for an identity that was reused.

CollectorCoverpoint / crossBin hit
resource_covcp_epoch_eventstale_return
resource_covx_stale_x_phasestale × operational — after the recovery completed
resource_covcp_epoch_eventrebaseline (at the re-advertisement)
resource_covcp_valueat_cap — exactly the advertisement, not more
traffic_covcp_stale_identityrejected
traffic_covcp_reuse_gapimmediate
traffic_covcp_legal_reorderyes
traffic_covx_reuse_x_reorderimmediate × yes
recovery_covcp_outstandingfew
recovery_covcp_response_in_flightyes
reliability_covcp_injectedF_FEEDBACK_LOST or F_DELAY
reliability_covcp_momentT_DURING_RECOVERY

Four readings.

x_stale_x_phase = stale × operational is the alarming cell, and it should be empty. A stale return during the recovery is expected; a stale return arriving after the link is operational again means the guard is being exercised in steady state, which points at an epoch advanced by something that should not advance it (19.6 §27's diagnostic-clear row). This is a bin whose presence is the finding, which §58's table has a column for.

immediate × yes is 19.2 §21's precondition exactly. An identity retired and immediately reused, plus a reordered response — neither coverpoint alone reaches it, and this is the cross's entire justification under §31.

cp_stale_identity.rejected records the guard working, not an error. Its emptiness would mean the generation guard has never been exercised, so 20.3 §20's uniqueness property is vacuous.

And T_DURING_RECOVERY is the moment bin that makes this scenario reachable deliberately. A feedback message dropped while the agreement is being re-established is the only way to produce the straggler window on purpose — which is why §25's fault-moment dimension exists rather than injecting on a timer.

50. Closure — Six Classifications

Every uncovered bin gets exactly one classification, and each has a different owner and a different next action. "Waived" is not one of them.

#ClassificationEvidence requiredOwnerAction
1stimulus missingnone — the default hypothesisDVwrite a sequence or relax a constraint within the plan (§56)
2the environment prevents itthe restricting component namedDVfix the environment
3the design prevents it — a bugthe architecture permits it; the design refusesdesigna defect found by coverage
4architecturally unreachablea proof (§51)architectureexclude with ignore_bins + the record (§15)
5the requirement is obsoletethe requirement removed from the planarchitectureremove the bin and the requirement row (§7)
6the coverage model is wrongthe bin's definition inspectedDVfix the bin

Four rules.

Classification 1 is the default and must be disproved, not assumed away. Most uncovered bins are missing stimulus, and that is the cheapest thing to fix. A bin jumping straight to classification 4 without a proof is a waiver wearing a classification's name.

Classification 3 is the one people forget coverage can produce. A bin the architecture permits and the design cannot enter is a defect — and it is discovered by a mechanism that never fails a test (§4).

Classification 5 must remove the requirement row too. A bin deleted while its requirement stays in §7's table leaves a requirement with no coverage item, which is exactly the gap that table exists to make visible.

And every classification is recorded with the bin, in the plan, not in a meeting. §52 is the record.

51. Unreachability Requires a Proof

"Never hit in regression" is not a proof of unreachability. It is classification 1.

Four acceptable proofs:

ProofExample
a formal unreachability resulta bounded or complete proof that the state is not reachable (20.3 §62)
an architectural constraintthe combination contradicts a stated contract
a parameter legality rulethe configuration is illegal by 19.6 §13's legality function
a specification exclusionthe specification forbids it — with the clause cited

Three notes.

The formal proof is the strongest and it is often cheap for exactly these bins. Unreachability of a state combination is a small, bounded question — the kind 20.3 §62 lists as an excellent formal target — and it is far cheaper than months of regression evidence that proves nothing.

A parameter-legality proof is per build, so the exclusion must be per build too (§40). An exclusion applied globally because it holds in one configuration is wrong in the others.

And the proof has an expiry. An architectural constraint can change in the next revision; a parameter can be widened. §52's record carries a review version for exactly this reason — an unreachability proof against UCIe 2.0 is not automatically valid against 3.0.

52. The Waiver Record

Where an exclusion is genuinely correct, it needs six fields. Anything less is waiver sprawl.

FieldWhy
binprecisely which bin or cross cell — not "the recovery bins"
classificationwhich of §50's six
reasonone sentence, in architectural terms
evidencethe proof (§51), or the named environment restriction
ownera person, not a team
review versionthe revision or release at which this must be re-examined
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The record, in the source, next to the exclusion. §15's form, completed.
//
//   BIN:            x_before_after[WC_MIN × WC_MAX]
//   CLASSIFICATION: 4 — architecturally unreachable
//   REASON:         a recovery cannot widen from the minimum to the maximum
//                   width in one step in this architecture; renegotiation
//                   proceeds through the intermediate classes.
//   EVIDENCE:       architecture contract; formal unreachability proof
//                   <reference>, bounded to depth 40, converged.
//   OWNER:          <name>
//   REVIEW AT:      next specification revision adoption
ignore_bins min_to_max_in_one_step =
  binsof(cp_before) intersect {WC_MIN} && binsof(cp_after) intersect {WC_MAX};

Three properties.

The review version is what prevents permanent sprawl. A waiver with no expiry outlives the reason for it, and a coverage model accumulating unreviewed exclusions converges to 100% of nothing.

The owner is a person because a team owns nothing. When the review comes due, somebody must be asked.

And the record lives in the source, not in a spreadsheet. A waiver in a separate document drifts from the bin it waives — and the drift is invisible, because the coverage report shows the exclusion applied and the document shows a reason for a bin that no longer exists.

53. Coverage Merge, and What It Hides

Merging coverage across a regression is correct and it hides one specific thing.

What merging does correctly. Different tests contribute different bins; the union is the regression's coverage; and no single test needs to hit everything.

What it hides: a temporal combination that no single run achieved.

Worked. Test A covers cp_outstanding.many. Test B covers cp_outcome.restored_degraded. The merged report shows both bins covered.

And x_outstanding_x_outcome[many × restored_degraded] was never hit by either, because A recovered to the same width and B had nothing outstanding.

Three consequences.

A cross cannot be satisfied by merging its axes. The cell requires both conditions in one run, at one momentwhich is precisely what a cross is for, and it is why the interesting items in §44 are crosses rather than coverpoints.

So the plan must encode combinations explicitly as crosses or as scenario enumerations (§30). A pair of coverpoints and a hope that some test hits both together is not a coverage item.

And a merged 100% across coverpoints with empty cross cells is the most flattering possible report of an under-verified design. §58's table lists crosses as separate rows for this reason.

54. Seed Diversity

Ten thousand seeds do not replace targeted scenarios, and the reason is structural rather than statistical.

What random seeds do well. Explore within the space the constraints describe. Different orderings, different interleavings, different value combinations inside the declared ranges — and they find interactions nobody hypothesised, which is real value.

What they cannot do. Reach a scenario the constraints cannot express. 19.5 §42's batching deadlock needs a burst followed by silence — and a constraint set that keeps traffic flowing, which is what a throughput-oriented random test does, can run for a billion cycles without ever producing the quiet period. No seed helps.

Three examples of scenarios no seed count reaches:

ScenarioWhy random misses it
burst then silence (19.5 §42)the constraint set never stops traffic
a fault at the first object (§25)requires synchronising on "first"
a fault during a configuration commit (§25)requires synchronising on the commit window
an identity reused within one cycle of retirement (§29)requires driving the allocation immediately after a retirement
a persistent fault exhausting the retrain budget (§11)requires a fault that does not stop

And the correct division of labour: constrained random explores inside an architecture plan; directed scenarios reach the corners the plan names. A regression that is all random has an unreachable set it cannot enumerate; one that is all directed misses what nobody thought of.

55. Coverage-Guided Stimulus

The useful loop, and the abuse of it.

The useful loop:

  1. run the regression;
  2. read the uncovered list, classified per §50;
  3. for classification 1, ask what scenario would produce this bin?;
  4. write that scenario — a sequence, a fault-sequence synchronisation, a constraint that widens a legitimate range;
  5. rerun; the bin is hit and the scenario is now a permanent, named test.

Step 4's output is the valuable artefact, not the closed bin. A directed scenario written to reach a bin remains in the regression forever and exercises the checkers around it on every run.

Worked, on the highest-value gap in §44. recovery_cov.x_outstanding_x_outcome[many × failed] is empty. Step 3's question: what produces many outstanding obligations at a fatal end? A persistent fault, injected while traffic is in flight, that exhausts the retrain budget (20.2 §42). Step 4 writes that fault sequence — and the resulting test is the only test in the suite that exercises R12's obligation classification.

And the abuse: bending constraints to hit arbitrary bins. §56.

56. Wrong Closure — Loosening Constraints

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — the constraint is widened to make bins fill, not to reach a scenario.
class ucie_traffic_seq_bad extends uvm_sequence #(ucie_semantic_item);
  constraint c_class {
    // Originally: cls inside {[0:NUM_CLASSES-1]} with a realistic weighting.
    // "Loosened" to fill the class × occupancy cross faster:
    cls dist { [0:NUM_CLASSES-1] :/ 1 };      // uniform, unrealistic
    // And the burst length randomised far beyond anything a real client sends:
    burst_len inside {[1:1024]};
  }
endclass

Four consequences.

Coverage rises and confidence does not. The bins fill; the situations they represent are not situations the design will meet, so the checkers exercised around them were exercised on unrealistic traffic.

Realistic traffic patterns get less coverage. Uniform class distribution means the class mix a real client produces — heavily skewed — is now a rare case in the regression. The bins that matter for the shipping configuration are hit less often than before the change.

It can mask a real gap. A cross cell that was empty because realistic traffic never reaches it is now filled by unrealistic traffic. The gap is closed on paper and the question it represented is unanswered.

And it inverts the relationship between plan and measurement. Coverage exists to serve the verification plan. A constraint changed to serve the coverage number has made the measurement the goal — and §5's argument is that the measurement was never the goal.

The test for any constraint change: does the new constraint describe traffic the design must actually handle? If yes, the change is a plan improvement and the coverage gain is a side effect. If no, the coverage gain is the only effect.

57. Convergence

A concept, not a dashboard. The useful quantity is new meaningful bins per regression iteration, and it has three regimes.

RegimeReading
new bins arriving steadilythe plan is being worked; stimulus development is productive
no new bins, uncovered list shortapproaching maturity — check the remaining list against §50
no new bins, uncovered list longstimulus is stuck — the remaining bins need scenarios random traffic cannot reach (§54)

Two notes.

The third regime is the common one and it is diagnosable. A long uncovered list that stops shrinking means the remaining bins are all outside the constraint space. Adding seeds will not help; §55's loop will.

And a flat curve is not the same as closure. A regression that stopped finding new bins because its stimulus cannot reach the remaining ones looks exactly like a regression that has covered everything reachable — and the difference is the classification of the remaining list, which is the only thing that distinguishes them.

58. The Coverage Review Table

The chapter's centerpiece artefact. Every coverage item answers six questions, and an item that cannot fill all six columns does not belong in the model.

Coverage itemArchitectural questionSample eventCrossed withPaired checkerAn uncovered result means
cp_arcwere all lifecycle paths taken, including failures?phase transition20.2 §13a path — often a failure path — was never taken
cp_train_attempts.exhaustedis the retrain budget enforced?training outcome20.2 §24bounded-retry termination is untested
cp_outcome.none (config)can the link refuse an incompatible peer?config exchangereliability mode20.2 §20the refusal path has never executed
cp_commit_context.busyis the commit atomic under traffic?config commitoutcome20.3 §37R8 is untested; partial commit undetected
x_before_afteris capacity recomputed when width changes?recovery exit20.2 §28R10 untested; stale headroom undetected
cp_bucket.full / .near_fullare the capacity boundaries reached?push / popcredit20.3 §29overflow and off-by-one paths untested
cp_simultaneous.bothis the simultaneous update correct?push / pop19.5 §16the operating point was never reached
cp_partial_batch.partialdoes the flush timer ever fire?return emission19.5 §44the batching deadlock is unverified
cp_epoch_event.stale_returnis the epoch guard exercised?credit returnlink phase20.3 §28the guard has never been tested
x_stale_x_phase[stale × oper](inverted) is the epoch advanced wrongly?credit return20.3 §38a hit here is the finding, not a miss
cp_outstanding.manydo obligations survive a recovery?recovery entryoutcome, credit20.3 §38R5 is unverified
x_outstanding_x_outcome[many × failed]are live obligations classified on a fatal end?recovery exit20.2 §41R12 is unverified
cp_first_fault_already_set.yesis the fault record sticky-first?recovery entry20.3 §38last-capture would pass every test
cp_attempts_per_object.twois retry exercised?resolutionobjects per op20.3 §33R3's assertion is vacuous
cp_arrival_ratiowhich fault was actually injected?resolutionfamily20.4 §19injections are unattributable
cp_deliveries.zero_with_failureis explicit failure reported?terminal20.3 §33the failure path is untested
x_reuse_x_reorder[immediate × yes]can a stale response alias a reused identity?response20.3 §20the aliasing guard is unverified
x_activity_x_asymmetric[duplex × stalled]are the directions independent?acceptance20.3 §45a shared ready would pass
cp_asymmetric_exhaustion.one_exhaustedare credit pools per class?credit eventclass19.5 §16the scalar-pool failure is untested
x_family_x_momentdo faults at different moments behave?injection20.2 §43timing-dependent bugs untested
cp_concurrency_casedid the named races occur?derivedvariousthe hypothesised interactions never happened

Four properties of this table.

The last column is the reason the table exists. "An uncovered result means R5 is unverified" is actionable in a way that a percentage is not — and it is what turns §50's classification from a chore into a decision somebody can make.

One row is inverted. x_stale_x_phase[stale × operational] is a bin whose hit is the finding. A coverage model containing only bins-that-should-be-hit cannot express that, and the column makes the exception explicit.

Every row names a paired checker (§34). A row with an empty checker column is a situation being exercised and not verified.

And the table is the review gate. A proposed new coverage item that cannot fill the architectural-question column or the uncovered-meaning column is a bin somebody wanted rather than a question somebody has — which is how §32's model accumulates.

59. Closure Checklist

Coverage is being reviewed for sign-off. In order:

  1. Are the five levels reported separately (§6), or as one number?
  2. Does every requirement in §7's table have both a checker and a coverage item?
  3. Does every requirement have a negative test?
  4. Is any coverage item sampled on a clock rather than on its own event (§9)?
  5. Is any lifecycle question answered by a residency bin instead of a transition bin (§12)?
  6. Does every illegal_bins cite a specification clause, an architectural contract, a parameter rule, or an arithmetic impossibility (§13)?
  7. Does every ignore_bins carry the six-field record (§15, §52)?
  8. Does every cross answer §31's question with a named bug?
  9. Are there crosses with more than three dimensions, and can each justify itself?
  10. Are boundary values in their own single-value bins (§22), or inside ranges?
  11. Is any bin bound a literal rather than a parameter expression (§40)?
  12. Does any architectural coverage item sample a design-internal signal (§42)?
  13. Are white-box items in a separate, labelled collector (§42)?
  14. Is cp_simultaneous.both among the most-hit bins, or among the rarest (§21)?
  15. Is cp_outstanding.many non-empty — was recovery ever tested with work outstanding (§26)?
  16. Is x_outstanding_x_outcome[many × failed] non-empty (§27)?
  17. Is cp_partial_batch.partial non-empty (§23)?
  18. Is cp_outcome.none non-empty — has the link ever refused a peer (§16)?
  19. Was every fault family injected, and did the expected detector fire each time (§24)?
  20. Was every fault moment exercised, including the three that need synchronisation (§25)?
  21. Does every assertion have a covered antecedent and a non-zero pass count (§35)?
  22. Is any property counted as coverage (§35)?
  23. Is code coverage reported as evidence rather than as requirement coverage (§36)?
  24. Does any conclusion rest on toggle or FSM-arc coverage for a scenario question (§37)?
  25. Is every uncovered bin classified into one of §50's six, with no "waived" category?
  26. Does every classification-4 bin have a proof, not a regression history (§51)?
  27. Does every waiver have an owner and a review version (§52)?
  28. Is any cross cell being satisfied by merged axes rather than a single run (§53)?
  29. Has any constraint been widened to fill bins rather than to describe real traffic (§56)?
  30. Is the uncovered list shrinking, and if not, is it short or long (§57)?

60. Common Misconceptions

"100% coverage means verified." It means the model's bins were hit. A poor model's 100% and a careful model's 80% are not comparable numbers, and the second is usually the more informative result (§5).

"More bins means a stronger plan." 1.7 million bins is a weaker plan than three thousand, because the shortfall in the large one cannot be classified and the effort it consumes is the effort the useful bins needed (§32, §45).

"Cross everything — you never know what interacts." You do know, and §31 is the question that establishes it. A cross with no named bug produces cells nobody can interpret and closure that is bin-count management.

"Code coverage is requirement coverage." It is implementation evidence. 19.5 §42's deadlock executes no unusual line and reports as fully covered (§36).

"illegal_bins can describe anything the RTL cannot do." Then the coverage model has encoded an implementation limit as a requirement, and the required path is never tested (§14).

"Unhit means unreachable." Unhit means classification 1 until proved otherwise. "Never seen in regression" is regression history, not a proof (§51).

"A waiver is a closure technique." A waiver without a classification, a proof, an owner and a review version is an undocumented reduction of the denominator (§52).

"Enough random seeds eventually hit every important race." Not one outside the constraint space. A burst followed by silence cannot be produced by a constraint set that keeps traffic flowing, at any seed count (§54).

"Coverage should sample every clock — more samples is more data." Then lifecycle bins measure residency and report 98% for the state the link idles in (§10).

"Assertions remove the need for functional coverage." An assertion whose antecedent never fires passes 100% of a regression. Coverage is what proves the assertion ran (§34, §35).

"Functional coverage checks correctness." It never fails. Correctness comes from assertions, scoreboards and models; coverage measures whether they had anything to check (§4).

"Every integer value deserves its own bin." Buckets plus boundaries beat enumeration: the boundaries are where bugs are and the middle is where redundancy is (§21, §22).

"Merged coverage proves the interactions occurred." A cross cell cannot be satisfied by merging its axes — it requires both conditions in one run (§53).

"The easiest way to close coverage is to loosen the constraints." It is, and it raises the number while reducing confidence, and it makes realistic traffic less covered than before (§56).

61. Understanding Check

62. Summary and What Comes Next

Functional coverage measures whether meaningful situations were observed. It never measures correctness — and a plan that confuses the two either exercises a design nothing checked, or checks a design in whatever situations happened to arise.

Read the model, not the number. 100% of a poor model and 80% of a careful one are not comparable, and the second is usually the more informative result.

Five levels, reported separately. Requirement coverage is the only one that can be complete; code coverage is a floor and never a ceiling; and scenario coverage is where UCIe verification actually lives.

Sample on the event the bin describes. A lifecycle bin sampled every clock measures residency and reports 98% for the state the link idles in.

Every cross names a bug. If the answer to "what breaks if these two interact wrongly?" is not a concrete failure, do not cross them — and the Cartesian product is 1.7 million uninterpretable cells that consume the budget the twelve useful ones needed.

An illegal_bins is a specification claim. Derived from what the current RTL supports, it encodes an implementation limit as a requirement and the required path is never tested.

Recovery context is the centerpiece. "A recovery occurred" captures none of the seven context dimensions or the three outcomes, and the cross of work-outstanding against outcome is where the two hardest requirements are verified.

Classify every uncovered bin into one of six. "Waived" is not one of them, unreachability needs a proof rather than a regression history, and every exclusion carries an owner and a review version.

And coverage serves the plan, never the reverse. A constraint widened to fill bins raises the number, reduces confidence, and makes realistic traffic less covered than it was before.

The plan now knows what must be checked, what must be exercised, and how to tell whether it was. What it does not yet have is the machinery that runs it — the agents that drive the boundaries, the peer that must be adversarial rather than accommodating, the sequences that compose scenarios across several interfaces at once, the coordinator that distributes the meaning of a reset rather than its wire, and the drain condition that stops a test from passing with thirty obligations still live. The next chapter assembles all of it into one reusable environment, without letting the framework's mechanics obscure the protocol reasoning these five chapters established.

Browse the full path on the UCIe tutorials index.