Skip to content

UVM RAL · Chapter 10 · RAL Coverage

Built-in RAL Coverage

RAL ships built-in coverage models so you get register coverage without hand-writing a single covergroup. It can collect register-bit coverage, address-map coverage, and field-value coverage automatically. But there is one fact that trips up everyone the first time. Built-in coverage is off unless you turn it on, and turning it on has two gates that both must be open. At build time the coverage model must be built into the register model, by including coverage before build runs. At run time sampling must be enabled on the instance. Miss the build gate and the covergroup is never created, so there is nothing to sample. Miss the run gate and it exists but never samples. Either way coverage reports a flat 0 percent that looks like weak tests but is really coverage never enabled. This lesson explains both gates and breaks that exact bug.

Foundation12 min readUVM RALcoverageinclude_coverageset_coverageUVM_CVR

Chapter 10 · Section 10.1 · RAL Coverage

1. Why Should I Learn This?

RAL's built-in coverage gives you register-bit, address-map, and field-value coverage for almost no effort — but only if you enable it, and enabling it has two separate gates that both must be open. Knowing that coverage must be built in at build time and sampling-enabled at run time — and that missing either yields an identical, misleading 0% — is what stops you from either wasting the free coverage or, worse, misreading a not-enabled 0% as a test-quality problem.

Learning the two-gate enabling of built-in coverage opens Chapter 10 and is the mechanism every later coverage topic (register/field coverage 10.2, user-defined 10.3, strategy 10.4, closure 10.5) depends on. It also complements the coverage honesty of 8.5: that was about what a sample means; this is about turning the sampling on at all.

2. Industry Story — the 0% that wasn't the tests' fault

A team runs a full register regression and pulls the built-in RAL coverage report: 0%, flat across every register. The immediate reading is 'our tests are terrible — they are not exercising the registers,' and effort is redirected into writing more register stimulus. More tests are added. Coverage stays 0%.

The tests were fine; coverage was never turned on. The register model had been built without coverage included (include_coverage was never set before build()), so the coverage covergroups were never constructed — gate one was closed. With no covergroup to sample, no amount of stimulus could ever move the number off 0%. The wasted effort — writing more tests to fix a 0% that was not a stimulus problem — was the real cost, and it evaporated the moment coverage was actually enabled (include at build, set_coverage at run), at which point the existing tests immediately showed substantial coverage. The post-mortem lesson: built-in RAL coverage is off unless enabled through two gates — coverage must be built into the model (include_coverage before build) and sampling enabled on the instance (set_coverage) — and missing either yields a flat 0% that looks like weak tests but is really coverage never turned on; always confirm coverage is enabled before concluding anything about test quality from the number.

3. Concept — two gates: build it in, then enable sampling

Built-in RAL coverage collects register coverage without hand-written covergroups — but only when both enabling gates are open:

  • The built-in models. UVM_CVR_REG_BITS (register-bit access), UVM_CVR_ADDR_MAP (address-map access), UVM_CVR_FIELD_VALS (field values) — selectable coverage models RAL can collect automatically.
  • Gate 1 — build time: include the coverage. RAL only constructs the coverage covergroups for a model if coverage was included before the model is built. This is set via uvm_reg::include_coverage(...) (and has_coverage(...) checks whether a model type builds a given coverage) before build() runs. Closed gate 1 means the covergroup is never created — nothing exists to sample, ever.
  • Gate 2 — run time: enable sampling. A model built with coverage still does not sample until sampling is enabled on the instance, via set_coverage(...) (query with get_coverage(...)). Closed gate 2 means the coverage exists but never samples.
  • Same symptom either way: 0%. Missing either gate yields a flat 0% — indistinguishable from 'tests hit nothing' unless you check that coverage is actually enabled.

Here are the two gates in series — both must be open for coverage to accumulate:

Two-gate built-in coverage: include_coverage at build, set_coverage at run; miss either -> 0%not includedincludednotsetenabledGATE 1 (build): include_coverage before build() — RAL CONSTRUCTS the covergroupsGATE 1(build):include_coveragebefore…closed -> covergroup NEVER created; nothing to sample -> flat 0%closed ->covergroup NEVERcreated; nothingto sample ->…GATE 2 (run): set_coverage enables SAMPLING on the instanceGATE 2 (run):set_coverageenablesSAMPLING on…closed -> covergroup exists but NEVER samples -> flat 0%closed ->covergroupexists but NEVERsamples -> flat…both open -> eachaccess SAMPLEScoverage ACCUMULATES — now the number reflects the stimuluscoverageACCUMULATES —now the numberreflects the…
Figure 1 — built-in RAL coverage is a two-gate mechanism, and BOTH gates must be open. GATE 1 (build time): include_coverage before build() so RAL CONSTRUCTS the coverage covergroups into the model; if closed, no covergroup exists to sample — ever. GATE 2 (run time): set_coverage enables SAMPLING on the instance; if closed, the covergroup exists but never samples. Only with both open does an access SAMPLE and coverage ACCUMULATE. Missing EITHER gate yields a flat 0% that looks like weak tests but is really coverage-not-enabled — so confirm both gates before blaming the stimulus.

4. Mental Model — coverage must be manufactured, then switched on

5. Working Example — enabling both gates

Include coverage at build time, enable sampling at run time, and query to confirm:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// GATE 1 (build time): include the coverage models BEFORE the model is built, so RAL CONSTRUCTS the
// covergroups. Do this before build() runs (e.g. in the test/env before reg_model.build()).
uvm_reg::include_coverage("*", UVM_CVR_REG_BITS | UVM_CVR_FIELD_VALS | UVM_CVR_ADDR_MAP);
reg_model = my_reg_block::type_id::create("reg_model");
reg_model.build();      // covergroups are constructed HERE — only because coverage was included first
reg_model.lock_model();
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// GATE 2 (run time): enable SAMPLING on the instance. Built-with-coverage is not enough; it must be set.
void'(reg_model.set_coverage(UVM_CVR_REG_BITS | UVM_CVR_FIELD_VALS | UVM_CVR_ADDR_MAP));
// From now on, register accesses SAMPLE the built-in coverage. Miss this and the covergroup never samples.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// CONFIRM coverage is actually on — do not assume. Query the gates before trusting (or distrusting) a number.
if (!reg_model.has_coverage(UVM_CVR_REG_BITS))
  `uvm_warning("COV", "REG_BITS coverage was NOT built in — include_coverage missing before build (gate 1)")
if (reg_model.get_coverage(UVM_CVR_REG_BITS) == 0 /* and sampling not enabled */)
  `uvm_info("COV", "check set_coverage was called (gate 2) before blaming stimulus for 0%", UVM_LOW)

With both gates open — coverage included before build, sampling enabled at run — accesses accumulate coverage and the number reflects the stimulus. Miss either, and you get the flat 0% of the next section.

6. Debugging Session — a flat 0% from coverage never enabled

1

Built-in coverage reports a flat 0% not because tests are weak but because coverage was never enabled — the covergroup was never built (gate 1) or never sampled (gate 2)

TURN COVERAGE ON (BOTH GATES)
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The register model is built WITHOUT including coverage, and sampling is never enabled:
reg_model = my_reg_block::type_id::create("reg_model");
reg_model.build();          // BUG (gate 1): no include_coverage before build -> covergroups NEVER constructed
reg_model.lock_model();
// (and BUG gate 2: no reg_model.set_coverage(...) either)
// Tests run and access registers heavily, but built-in coverage stays flat 0% — nothing to sample.
Symptom

The regression runs, registers are accessed heavily, and the built-in RAL coverage report shows a flat 0% across every register. The natural — and wrong — conclusion is 'the tests are not exercising the registers,' prompting more stimulus to be written. But adding tests does nothing: coverage stays at 0%, because the problem is not the stimulus. The tell, invisible unless you check, is that querying has_coverage/get_coverage shows coverage was never built in or never enabled — the 0% is a not-enabled artifact, identical in appearance to a weak-tests 0% but entirely different in cause.

Root Cause

Built-in coverage is a two-gate mechanism and both gates were closed. Gate 1 (build time): include_coverage was never called before build(), so RAL never constructed the coverage covergroups into the model — there is literally no covergroup to sample, so no access can ever record coverage, regardless of how many tests run. Gate 2 (run time): even had the model been built with coverage, set_coverage was never called, so sampling would not be enabled on the instance. With no meter built and no switch flipped, the number is structurally 0% — it reflects the absence of coverage collection, not the absence of stimulus. The reason it masquerades as a test-quality problem is that a not-enabled 0% is pixel-for-pixel identical to a genuine 'tests hit nothing' 0%; the number alone cannot distinguish 'coverage off' from 'coverage on but unhit,' so the instinct to blame the tests is a category error — the measurement was never turned on.

Fix

Open both gates. Gate 1: call uvm_reg::include_coverage(...) (with the desired UVM_CVR_* models) before the model is built, so RAL constructs the covergroups. Gate 2: call set_coverage(...) on the model instance at run time so sampling is enabled. Then confirm with has_coverage/get_coverage that coverage is actually built and on. With both open, the existing tests immediately show real coverage — no new stimulus needed. The rule the bug teaches: built-in RAL coverage is off unless enabled through both gates — included at build (include_coverage before build()) and sampling-enabled at run (set_coverage) — and a flat 0% is identical whether coverage is disabled or tests are weak, so always confirm coverage is enabled before concluding anything about test quality. The first response to a 0% built-in coverage number is 'is it turned on?', not 'are my tests bad?'.

7. Common Mistakes

  • Reading a flat 0% as weak tests. A not-enabled 0% is identical to a genuine 0% — confirm coverage is built in and sampling-enabled before blaming stimulus.
  • Building the model without including coverage. include_coverage must be called before build(), or the covergroups are never constructed (gate 1) and nothing can ever sample.
  • Building with coverage but never calling set_coverage. The covergroup exists but never samples (gate 2) — enable sampling on the instance.
  • Assuming coverage is on by default. Built-in coverage is off by default (it costs memory/time); both gates are opt-in.
  • Not querying has_coverage/get_coverage. Confirm both gates are open rather than assuming — the query distinguishes a disabled meter from an unhit one.

8. Industry Best Practices

  • Enable both gates explicitly and early. include_coverage before build, set_coverage at run — and make it part of the standard env setup so it is never forgotten.
  • Confirm coverage is on with has_coverage/get_coverage. Verify the meter exists and is enabled before trusting or distrusting any number.
  • Treat a 0% as 'is it enabled?' first. Rule out the disabled-coverage artifact before attributing 0% to test quality.
  • Select only the UVM_CVR_* models you need. Built-in coverage costs memory and simulation time; include what you will actually use.
  • Document the coverage-enable step. Because it is opt-in and its absence is a silent 0%, make the two-gate enable visible and reviewed.

9. Interview / Review Questions

10. Key Takeaways

  • RAL provides built-in coverage modelsUVM_CVR_REG_BITS (register bits), UVM_CVR_ADDR_MAP (addresses), UVM_CVR_FIELD_VALS (field values) — giving register coverage without hand-written covergroups.
  • Built-in coverage is off by default and gated in two places, both of which must be open: gate 1 (build)include_coverage before build() so RAL constructs the covergroups; gate 2 (run)set_coverage so sampling is enabled on the instance.
  • Closed gate 1 means the covergroup is never created (nothing to sample, ever); closed gate 2 means it exists but never samples — and either yields an identical flat 0%.
  • A not-enabled 0% is indistinguishable from a weak-tests 0% by the number alone — so the first response to a flat 0% is 'is coverage enabled?', not 'are my tests bad?'.
  • Confirm both gates with has_coverage/get_coverage, enable coverage as a standard, reviewed env step, and select only the UVM_CVR_* models you need (coverage costs memory and simulation time).