TO Be coming

Nov 12, 2026

UVM Course : Day 10: Functional Coverage — Covergroups, Coverpoints, Crosses, and Verification Plans

Day 10: Functional Coverage — Covergroups, Coverpoints, Crosses, and Verification Plans
Advertisement
Top banner — drop your 728×90 or responsive ad unit here.

Day 10: Functional Coverage — Covergroups, Coverpoints, Crosses, and Verification Plans

A 30-day SystemVerilog/UVM verification series. The scoreboard tells you what passed. Coverage tells you what you never tried.

A scoreboard answers one question: on the transactions the DUT saw, did it behave correctly? It does not answer the question that matters just as much: did the testbench actually try the conditions that matter? A testbench that writes 0x00 to a FIFO 10,000 times will produce a perfectly green scoreboard and verify almost nothing. That gap is what functional coverage exists to close.

Coverage is a declaration of intent. You write down, in code, the conditions you meant to exercise. The simulator records which ones you hit. Coverage closure is the process of finding the zeros — the conditions you meant to hit and never did — and adding sequences to reach them.


1. Covergroups and Coverpoints

A covergroup is a class-like construct that samples values and records which bins they landed in. A coverpoint is one variable whose values you want to track. Bins can be auto-generated or explicitly declared.

class fifo_coverage extends uvm_subscriber #(fifo_tx);
    `uvm_component_utils(fifo_coverage)
    fifo_tx tr;
    covergroup cg_fifo;
        option.per_instance = 1;
        cp_wr_en: coverpoint tr.wr_en;
        cp_rd_en: coverpoint tr.rd_en;
        cp_wdata: coverpoint tr.wdata {
            bins zero     = {8'h00};
            bins max      = {8'hFF};
            bins low      = {[8'h01:8'h7F]};
            bins high     = {[8'h80:8'hFE]};
        }
    endgroup
    function new(string name, uvm_component parent);
        super.new(name, parent);
        cg_fifo = new();
    endfunction
    function void write(fifo_tx t);
        tr = t;
        cg_fifo.sample();
    endfunction
endclass

Three things to notice:

  • uvm_subscriber is the UVM class designed for exactly this. It has an analysis_export out of the box, so you connect it like a scoreboard.
  • Explicit bins (zero, max, low, high) map value ranges to named conditions. Auto bins would give you one bin per value, which is useless for an 8-bit field.
  • sample() is called explicitly, once per transaction. Sampling inside write() keeps the coverage in sync with the scoreboard.

2. Cross Coverage

Coverpoints verify individual conditions. Crosses verify combinations. A FIFO that has been written-with-zero and read-from-max might have hit both coverpoints independently while never having been full at the moment a read was attempted. That specific bug — read-from-empty — only shows up when you cross wr_en, rd_en, and the DUT's empty flag.

cross_wr_rd: cross cp_wr_en, cp_rd_en {
    // The interesting combos
    bins wr_only  = binsof(cp_wr_en) intersect {1} &&
                    binsof(cp_rd_en) intersect {0};
    bins rd_only  = binsof(cp_wr_en) intersect {0} &&
                    binsof(cp_rd_en) intersect {1};
    bins idle     = binsof(cp_wr_en) intersect {0} &&
                    binsof(cp_rd_en) intersect {0};
    // Simultaneous write+read: usually illegal, check the spec
    bins both     = binsof(cp_wr_en) intersect {1} &&
                    binsof(cp_rd_en) intersect {1};
}
Why crosses catch bugs that coverpoints miss: independent coverpoints tell you that each corner was hit at least once, not that they were hit together. Real bugs live in the togethers — full-and-read, empty-and-write, back-to-back-transition. Crosses are how you state those togethers.

3. Sampling at the Right Time

Coverage is only as good as when it samples. Sample too early and you record stimulus that never reached the DUT. Sample too late and you record post-correction values that hide the bug.

In a UVM subscriber, the monitor publishes a transaction after the DUT has responded. Sampling in write() therefore samples post-response values — exactly right for a checker but potentially wrong for a coverage model that wants to record the stimulus.

Sample where…What you recordGood for
Driver's analysis portWhat the testbench intended to driveStimulus coverage ("did we try this?")
Monitor's analysis portWhat the DUT actually sawBehavioural coverage ("did it reach the pins?")
Inside a sequenceRandomization-time valuesEarly sanity — rarely useful as the primary source
Common mistake: sampling in run_phase with @(posedge clk) and no guard, which produces a coverage sample every clock — usually dominated by idle cycles that bury the interesting bins under noise. Always sample once per transaction, not once per clock.

4. Tying Coverage to a Verification Plan

Coverage without a plan is just a number that goes up. A verification plan lists the features and corner cases the DUT is required to handle, and each line of the plan maps to one or more coverpoints or crosses. Coverage closure is then "every plan line hit," not "overall coverage is 90%."

Plan lineCovergroup binStatus
Write at fullcp_full / binsof(cp_full) intersect {1} when wr_enCloses
Read at emptycross_wr_rd / rd_only when emptyCloses
Wrap-aroundcp_wr_ptr from max to zeroOpen
Simultaneous write+readcross_wr_rd / bothCloses

When the run ends, run uvm_report_phase or use $coverage_get / $coverage_save to dump the coverage database, and cross-reference each plan line against the report. A plan line that is "closed by inspection" without a corresponding bin is a lie you are telling yourself.


5. Coverage-Driven Stimulus

Once coverage is measured, the test can be steered. Two standard techniques:

  • Constrained-random with coverage feedback — run a random regression, look at the coverage report, and add targeted sequences (or tighten constraints) to the holes.
  • Explicit directed sequences — for rare conditions (wrap-around, simultaneous write-read, near-full burst), write a sequence that hits it directly rather than hoping randomization lands there.

The most efficient teams do both: random regression for breadth, targeted sequences for corners, and coverage is the scoreboard that tells them when to stop.


6. Common Coverage Bugs

SymptomCause
Overall coverage high but a known corner never hitNo cross for the corner — individual coverpoints hit it separately
Coverage says 100%, bug found in regressionCoverage model doesn't describe the bug's condition (missing coverpoint)
Sample count enormous, bins sparseSampling per clock instead of per transaction
Bins never hit even with obvious trafficsample() never called, or called with tr still null
Coverage passes in simulation but not in the reportCoverage database not saved ($coverage_save) or merged across tests
Adding coverage slows simulation 10×Per-bin callbacks in a tight loop — use option.per_instance = 0 where possible and sample at transaction boundaries

7. What Day 11 Will Add

Tomorrow: Callbacks and Synchronization. uvm_callback lets you extend a component's behaviour without subclassing it, uvm_event lets components signal each other without knowing their paths, and uvm_barrier synchronizes multiple processes on a shared event. Together they are the escape hatch for the cases where the layered architecture is not enough.


8. Quiz

Ten questions on covergroups, coverpoints, crosses, sampling, and closure.

Question 1 of 10
Loading…
    0 / 10
    Advertisement

    Coming Up Next: Day 11 — Callbacks and Synchronization

    • uvm_callback — extending component behavior without subclassing, and where callbacks live in the component tree.
    • uvm_event — signaling between components without them knowing each other's paths.
    • uvm_barrier — synchronizing N processes on a single event, useful for multi-agent coordination.
    • When not to use them — callbacks and events are escape hatches; using them everywhere makes the architecture opaque.
    ← Back to the 30-Day UVM Course
    See all 30 days, week by week.

    Series: 30 days of SystemVerilog/UVM verification. Day 1 built the skeleton. Day 2 wired the lifecycle. Day 3 put it in motion. Day 4 wired the plumbing. Day 5 installed the knobs. Day 6 made it modular. Day 7 made it yours. Day 8 made it coordinated. Day 9 made it rigorous. Day 10 made it measurable.

    No comments:

    Post a Comment

    SiteMap