Skip to content

UVM RAL · Chapter 10 · RAL Coverage

Coverage Collection Strategy

Knowing how to enable, interpret, and wire register coverage still leaves a strategic question: where, when, and what should you actually collect so the number is both cheap enough to afford and meaningful enough to trust. Two axes drive the strategy. The first is cost, since every enabled model on every register consumes memory and simulation time, so you select the models and scope you will actually analyze rather than collecting coverage nobody reads. The second is attribution, which is subtler and more dangerous. Coverage is only meaningful when credited to real functional stimulus, not to the scaffolding of backdoor pokes, reset reads, and setup writes. Sampling indiscriminately lets that non-functional traffic fill the bins and read closed. The lesson ends with the signature bug where backdoor setup pokes inflate coverage to a false closed, hidden until you gate sampling to functional accesses.

Foundation12 min readUVM RALcoveragecollection strategyattributionbackdoor setup

Chapter 10 · Section 10.4 · RAL Coverage

1. Why Should I Learn This?

Coverage that is collected everywhere, always is both expensive (it slows regressions and bloats memory) and dishonest (setup and backdoor traffic inflate the bins, so the number overstates what functional stimulus achieved). Knowing to select the models and scope you will analyze — and to gate sampling to real functional accesses so coverage is credited honestly — is what makes the number both affordable and trustworthy.

Learning coverage collection strategy — cost selection and honest attribution — is what turns the mechanisms of 10.1–10.3 into a coverage number you can sign off on, and it directly precedes analysis and closure (10.5), which are only meaningful if the number was collected honestly in the first place.

2. Industry Story — coverage 'closed' by the setup code

A team reaches 100% on a set of critical field-value bins and declares that coverage closed. But the design still had bugs behind those exact values, which was baffling — the coverage said those values were exercised.

They were exercised — by the setup code, not the functional tests. To reach interesting states quickly, the tests used backdoor pokes (8.4) to seed registers, and the coverage was sampled on every update, including those backdoor pokes. So the seeding traffic filled the critical bins: the covergroup recorded the reserved and boundary values because the setup drove them into the model, even though the functional stimulus — the actual bus-level tests meant to verify behavior — never drove those values through the real path. Coverage read closed, but the credit belonged to the scaffolding. When the team disabled coverage during setup and re-ran, the same bins dropped to a much lower number, revealing that the functional tests had not actually achieved the coverage — the setup had inflated it. The bugs behind those values escaped because no functional test ever exercised them; only the backdoor setup had. The post-mortem lesson: coverage must be credited to real functional stimulus, not to setup/backdoor/reset scaffolding — sampling indiscriminately on every access lets seeding traffic fill the bins and inflates coverage to a false 'closed'; gate sampling to meaningful functional accesses (or disable it during setup) so the number reflects what verification actually earned.

3. Concept — select for cost, gate sampling for honest attribution

A coverage collection strategy makes two kinds of decision:

  • Cost — select models and scope. Enabling every UVM_CVR_* model and every user covergroup on every register everywhere costs memory (covergroup structures) and simulation time (sampling per access). Select the models and scope (registers/blocks) that will actually be analyzed; do not collect coverage nobody reads.
  • Attribution — gate sampling to meaningful accesses. Coverage is only meaningful if credited to real functional stimulus. Non-functional accesses — backdoor pokes that seed state (8.4), reset reads, configuration scaffolding — should not fill the functional bins. Options:
    • disable coverage sampling during setup (turn it off while seeding, on for the functional body), or
    • sample only the meaningful accesses (e.g. gate the sample on frontdoor/functional traffic, exclude backdoor/setup).
  • The failure: inflation/misattribution. Sampling indiscriminately lets setup/backdoor traffic fill the bins, so coverage reads closed while the functional stimulus never achieved it — green earned by plumbing, overstating verification.
  • The test of honesty. If disabling coverage during setup (or excluding backdoor) drops the number, the collected coverage was inflated — the real number is the lower one.

Here is the strategy as a flow — select what to collect, then gate when to sample:

Coverage strategy: select models/scope for cost, gate sampling to functional accesses (exclude setup/backdoor) for honest attributionscaffoldingfunctionalSELECT models + scope you will actually analyze (cost: memory + sim time)SELECT models +scope you willactually analyze(cost: memory +…GATE sampling: is this a REAL FUNCTIONAL access, or setup/backdoor/reset scaffolding?GATEsampling: isthis a REALFUNCTIONAL…setup/backdoor -> do NOT sample (else it INFLATES the bins)setup/backdoor-> do NOT sample(else itINFLATES the…functional ->sample: creditbelongs to realstimulusHonesty test: disabling coverage during setup should NOT drop the numberHonesty test:disablingcoverage duringsetup should NO…
Figure 1 — a coverage collection strategy has two decisions. COST: select which coverage models and which register/block SCOPE you will actually analyze — enabling everything everywhere burns memory and sim time. ATTRIBUTION: gate sampling so only REAL FUNCTIONAL stimulus fills the bins — disable coverage (or exclude the sample) during setup/backdoor/reset scaffolding, because seeding pokes (8.4) that fill functional bins INFLATE coverage to a false 'closed'. Honesty test: if disabling coverage during setup DROPS the number, it was inflated — the real coverage is the lower one that functional stimulus earned.

4. Mental Model — coverage is a credit ledger; credit only the work that counts

5. Working Example — selecting scope and gating sampling to functional stimulus

Enable coverage for the scope you will analyze, and gate sampling so setup/backdoor traffic does not inflate it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// COST: select the models and scope you will actually analyze — not every model on every register.
uvm_reg::include_coverage("*", UVM_CVR_FIELD_VALS);       // the model you will close on (10.1)
// (scope down to the blocks under analysis rather than the whole SoC register model)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ATTRIBUTION: gate sampling so ONLY functional stimulus fills the bins. Disable during setup/backdoor.
task run_phase(uvm_phase phase);
  cov_enabled = 0;                       // OFF during setup: seeding must NOT be credited
  seed_state_via_backdoor();             // backdoor pokes (8.4) — these must not fill functional bins
  cov_enabled = 1;                       // ON for the functional body: this is the real stimulus
  run_functional_stimulus();
endtask
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The sample point respects the gate — and can also exclude non-functional access PATHS:
virtual function void post_predict(...);
  if (!cov_enabled) return;              // skip setup-phase updates
  if (path == UVM_BACKDOOR) return;      // credit only real (frontdoor/functional) accesses, not backdoor
  cg.sample(value[2:0], dma_field.get());
endfunction
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// HONESTY TEST: disabling coverage during setup should NOT drop the number. If it does, it was inflated.
// Re-run with cov_enabled forced 1 during setup vs gated: if the gated number is LOWER, setup was
// filling the bins -> the real coverage is the gated (lower) one that functional stimulus earned.

Selecting the model/scope keeps the cost bounded; gating the sample to the functional body (and excluding backdoor paths) keeps the number honest — credited to real stimulus. The next section is what happens when that gate is missing.

6. Debugging Session — backdoor setup inflating coverage to 'closed'

1

Sampling coverage on every access lets backdoor setup pokes fill the functional bins, so coverage reads closed though the functional stimulus never achieved it

CREDIT ONLY FUNCTIONAL STIMULUS
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Coverage is sampled on EVERY update, including backdoor setup pokes used to seed state:
virtual function void post_predict(input uvm_reg_field fld, ...);
  cg.sample(value[2:0], dma_field.get());   // BUG: samples ALL updates — including backdoor SETUP pokes
endfunction
// Tests seed the reserved/boundary values via backdoor poke to reach states fast (8.4).
// Those setup pokes FILL the critical bins -> coverage reads CLOSED, but functional stimulus never drove them.
Symptom

The critical field-value bins reach 100% and coverage is declared closed. Yet bugs behind exactly those values escape — which is baffling, because coverage says the values were exercised. The values were exercised, but by backdoor setup pokes that seeded state, not by functional stimulus verifying behavior. The tell: if you disable coverage during setup (or exclude backdoor accesses) and re-run, the same bins drop to a much lower number — proving the closure was bought by setup traffic, not earned by the functional tests. Nothing in the raw number reveals this; it looks like legitimately-closed coverage.

Root Cause

Coverage was sampled indiscriminately on every access, with no distinction between functional stimulus and setup scaffolding. The tests used backdoor pokes to seed registers into interesting states quickly (8.4), and those pokes update the model's field values, firing post_predict and thus cg.sample — so the setup traffic filled the critical bins. The coverage number therefore reflected values driven by seeding, not by the functional tests whose job was to verify the design. This is misattribution: the credit for the covered values belongs to the plumbing (backdoor setup), not to verification, so the number overstates what was actually verified. The functional bug escaped because no functional test ever drove those values through the real path — only the backdoor setup did — and backdoor accesses bypass the very logic under test. The number was inflated precisely because the sampling did not gate on whether an access counted as verification work.

Fix

Gate sampling so only real functional stimulus is credited: disable coverage during setup (sample off while seeding, on for the functional body) and/or exclude non-functional access paths from the sample (skip UVM_BACKDOOR accesses, skip setup-phase updates). Re-run; the number now reflects what the functional tests actually achieved — likely lower, and honest — exposing the real holes (the values functional stimulus never drove) so they can be covered with real tests. Apply the honesty test routinely: disabling coverage during setup should not drop the number; if it does, the coverage was inflated. The rule the bug teaches: coverage must be credited to real functional stimulus, not to setup/backdoor/reset scaffolding — sampling indiscriminately lets seeding traffic fill the bins and inflates coverage to a false 'closed'; gate sampling to meaningful functional accesses so the number reflects what verification earned, and use the disable-during-setup honesty test to detect inflation.

7. Common Mistakes

  • Sampling coverage on every access. Setup, backdoor, and reset traffic fill the bins and inflate coverage — gate sampling to real functional stimulus.
  • Crediting backdoor seeding as coverage. Backdoor pokes bypass the logic under test; letting them fill functional bins overstates verification (8.4).
  • Enabling every model on every register everywhere. Coverage costs memory and sim time — select the models and scope you will actually analyze.
  • Collecting coverage nobody reads. Coverage you never close on is pure cost — collect what you will analyze.
  • Skipping the honesty test. If disabling coverage during setup drops the number, it was inflated — check it routinely.

8. Industry Best Practices

  • Select coverage models and scope deliberately. Collect the UVM_CVR_* models and user covergroups, on the blocks, you will actually analyze — not everything everywhere.
  • Gate sampling to functional stimulus. Disable coverage during setup/seeding and exclude backdoor/reset access paths, so credit belongs to verification.
  • Apply the disable-during-setup honesty test. Confirm setup traffic is not inflating the number; if it is, gate it out and use the lower, honest figure.
  • Attribute coverage to the stimulus that earns it. A bin is meaningful only if functional stimulus filled it — not scaffolding.
  • Balance cost against value. Enable the coverage that informs closure; drop coverage that only burns memory and simulation time.

9. Interview / Review Questions

10. Key Takeaways

  • Coverage collection is a strategy, not just a mechanism — decide where, when, and what to collect so the number is both affordable and trustworthy.
  • Cost axis: enabling every model on every register everywhere burns memory and simulation timeselect the models and scope you will actually analyze; do not collect coverage nobody reads.
  • Attribution axis: coverage must be credited to real functional stimulus, not to setup/backdoor/reset scaffolding — sampling indiscriminately lets seeding traffic fill the bins and inflate coverage to a false 'closed.'
  • The signature bug is backdoor setup pokes filling functional bins (8.4): coverage reads closed while functional stimulus never drove those values, so bugs behind them escape — backdoor bypasses the logic under test, so its credit is meaningless as verification.
  • Gate sampling to functional accesses (disable during setup, exclude backdoor paths) to keep coverage honest without losing backdoor setup's speed, and apply the disable-during-setup honesty test: if the number drops, it was inflated — the real coverage is the lower one.