Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
    • Scoreboard Architecture: Expected vs Actual
    • Reference Models: Predictors, DPI-C and Built-in Comparators
    • Coverage & Closure
      • Designing a Coverage Model from the Spec
      • Coverage Components and Sampling
      • Bins, Illegal Bins and Ignore Bins
      • Crosses and Plan Traceability
      • Coverage Options That Change the Numbers
      • SVA cover property vs Covergroups
      • Regression Merge and the Closure Loop
      • Closing Coverage: The Holes Random Never Hits
      • Reporting and Verbosity Control
      • Code Coverage and How It Closes with Functional Coverage
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Coverage & Closure
  1. Topics
  2. UVM Verification
  3. Checking & Coverage in UVM
  4. Coverage & Closure
  5. SVA cover property vs Covergroups

Part 8 · Checking & Coverage · Intermediate

SVA cover property vs Covergroups

Temporal coverage of stalls and wait states with cover property in the interfaces, versus value coverage in classes.

On this page

    The plan row reads: "the output stalls for three or more cycles while TVALID is held". The coverage report shows gap=4+ hit in dma_check_test, and someone ticks the row. But in that test the output's TREADY is tied high: nothing ever stalled. The long gaps were the input driver resting between beats. The covergroup measured a number between two transfers; the plan row describes a shape in time.

    Shapes in time are what SystemVerilog Assertions describe. When should a plan row become a cover property instead of a bin, where does it live, and how do its results reach the same closure report?

    The idea: a photograph versus a video

    A covergroup is a photograph: at one sample point it records which values were present. A `cover property` is a video clip search: it watches every clock and records each time a sequence (a pattern of signal values over consecutive cycles) completes. "LEN was 16 when GO was written" is a photograph. "TVALID stayed high while TREADY was low for three clocks, then the beat moved" is a clip.

    The split also decides where the coverage lives. Concurrent assertions and cover properties cannot be written inside a class. They live in modules, interfaces or checkers, usually in or bound to the interface whose signals they watch. Value coverage of transactions lives in a class, fed by a monitor.

    The picture: one stall that a cover property sees

    The stream monitor publishes one item, at the edge where TVALID and TREADY are both high. Everything interesting about this picture happened before that item existed, so no covergroup sampling monitor items can see it.

    Build it: temporal coverage in the interfaces

    Step 1: a stall cover for the AXI-Stream interface

    A checker module with the cover properties, bound into every instance of axis_if. Binding keeps the interface file unchanged and gives every instance its own copy, named by the hierarchy (top.s_ax.u_cover.c_stall3, top.m_ax.u_cover.c_stall3), so the distinct-name problem of covergroups does not arise.

    axis_cover.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // not-run: a cover-property module, shown as an excerpt (Verilator records covers only with --coverage-user)
    module axis_cover (input logic clk, rst_n, tvalid, tready);
      // TVALID held through 3 clocks of TREADY low, then the transfer
      c_stall3: cover property (@(posedge clk) disable iff (!rst_n)
        (tvalid && !tready) [*3] ##1 (tvalid && tready));
      // two transfers on consecutive clocks
      c_b2b: cover property (@(posedge clk) disable iff (!rst_n)
        (tvalid && tready) ##1 (tvalid && tready));
      // the rule the stall cover depends on: TVALID never drops before the transfer
      a_hold: assert property (@(posedge clk) disable iff (!rst_n)
        (tvalid && !tready) |=> tvalid);
    endmodule
    
    bind axis_if axis_cover u_cover (.clk(clk), .rst_n(rst_n), .tvalid(tvalid), .tready(tready));

    Two details matter. (tvalid && !tready) [*3] ##1 (tvalid && tready) needs the transfer immediately after exactly three stalled clocks, so a stall of ten clocks matches once (from the attempt that starts three clocks before the end), and the hit count equals the number of stalls of three or more. Writing [*3:$] instead would match from every starting clock, inflating the count. And the cover sits beside the assertion it depends on: a cover proves the case happened, the assertion proves it was handled legally.

    Step 2: wait-state covers for APB

    apb_cover.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // not-run: a cover-property module, shown as an excerpt (Verilator records covers only with --coverage-user)
    module apb_cover (input logic clk, rst_n, psel, penable, pready);
      // an access with one wait state: PREADY low, then high
      c_wait1: cover property (@(posedge clk) disable iff (!rst_n)
        (psel && penable && !pready) ##1 (psel && penable && pready));
      // an access with no wait state
      c_nowait: cover property (@(posedge clk) disable iff (!rst_n)
        (psel && !penable) ##1 (psel && penable && pready));
    endmodule
    
    bind apb_if apb_cover u_cover (.clk(clk), .rst_n(rst_n), .psel(psel), .penable(penable), .pready(pready));

    dma_dut is built with WAIT_STATES = 1, so every access takes exactly one wait state. c_wait1 is hit by every register access, and c_nowait can never be hit in this configuration. That is not a stimulus hole: it is an unreachable item, and the merge lesson shows how it becomes a reviewed exclusion with the parameter as its reason.

    Step 3: keep value facts in the class

    None of this replaces dma_coverage. "Which LEN classes ran, with which IRQ_EN" is a value fact about one transaction, and a covergroup with bins and crosses is the right tool. The split, side by side:

    Covergroup in a class

    cover property in the interface

    Records

    Values at a sample point, binned and crossed

    Each completion of a sequence over clocks

    Fed by

    sample() calls from a monitor stream

    The clock, every cycle

    Good for

    Configuration, outcomes, value classes, combinations

    Stalls, wait states, handshake order, latency windows

    Instance names

    Must be set (set_inst_name)

    Come from the hierarchy automatically

    In the report

    Covergroup section, scored in %

    Assertion or directive section, as hit counts

    Merged by

    The vendor merge, by covergroup and instance

    The same merge, by hierarchical directive name

    A per-cycle covergroup in the interface sits between the two: it samples every clock like a property, but records values and simple transitions in bins (the handshake covergroup). Use it when you want a percentage over a few states; use a cover property when the plan row is a multi-cycle shape.

    Watch it run

    The course's simulator runs neither the cover properties nor the coverpoints, so there is no cover log to show here. What the real logs do show is which tests could ever hit c_stall3 on the output side:

    Diagram
    +UVM_TESTNAME=dma_backpressure_test   (+UVM_VERBOSITY=UVM_MEDIUM)
    UVM_INFO dma_tb.sv(1246) @ 1005000: uvm_test_top [BP] the slave stalled the DMA for 28 clocks
    UVM_INFO dma_tb.sv(411) @ 1005000: uvm_test_top.env.sb [SB] 12 words matched, 0 mismatched
    • Only dma_backpressure_test replaces the always-ready output with a reactive slave that drives TREADY low. Every other test leaves TREADY tied high, so the output-side c_stall3 is empty there by construction.

    • 28 is the total number of stalled clocks, not the length of any one stall. Whether any single stall lasted three clocks with TVALID held is exactly the question the cover property answers and the log cannot.

    • When the output stalls, the DMA's one output register fills and it drops TREADY on its input, so the input-side cover can be hit by the same test.

    What breaks: the stall that was really an idle

    The opening story is a real reading of this bench's logs. dma_check_test hits gap=4+, and its output TREADY never goes low. The long gaps come from the input driver's idle knob, up to 8 clocks with TVALID low. A gap between completed beats lumps two different situations together: the source had nothing to send, or the sink refused it. For the plan row "backpressure loses nothing", only the second counts, and only a check of TVALID and TREADY during the gap can tell them apart.

    The fix is a modelling decision. Keep cp_gap for what it measures (input pacing) and give the backpressure row its own temporal item, c_stall3 on the output interface, mapped in the traceability table. Then the regression shows the truth: covered only by dma_backpressure_test.

    Interview

    "When do you use a cover property instead of a covergroup?"

    A weak answer: "Cover properties are for assertions people, covergroups for testbench people." That describes an org chart, not a decision.

    A strong answer: "It depends on the shape of the fact. A value at one moment (a length class, an outcome, a combination) is a covergroup in a class, fed by a monitor. A pattern over several clocks (a stall held for N cycles, a wait state, a request followed by a grant within a window) is a cover property in or bound to the interface, next to the assertion that checks the same behaviour. Both land in the coverage database: covergroups as percentages, covers as hit counts per hierarchical name, merged by the same tool. On our DMA, a gap covergroup looked like backpressure coverage, but the gaps were source idles; only a cover on TVALID high with TREADY low measures a stall."

    Expect the follow-ups: Can you put a cover property in a UVM monitor? (no: concurrent assertions are not allowed in classes; bind a checker to the interface, or have the monitor publish a measured stall length for a covergroup) and What does a cover hit not prove? (correctness; that is the assertion's job).

    Check yourself The output-side c_stall3 is empty after a full regression. List two different explanations and how to tell them apart.

    Either no test stalls the output for three clocks (a stimulus hole: check whether any test uses the reactive slave, and its stall settings), or the binding or reset gating is wrong (a model bug: check that c_stall3 exists in the database for top.m_ax, and that rst_n is high during traffic). An empty item that is missing from the database is a model bug, not a hole.

    Check yourself Why does c_stall3 use [*3] ##1 rather than [*3:$]?

    [*3] ##1 transfer requires the transfer right after exactly three stalled clocks, so each stall of three or more matches once. [*3:$] matches from every starting clock that leaves at least three stalled cycles, so one long stall produces many hits, and the count no longer means "number of stalls".

    Check yourself Is c_nowait on the DMA's APB interface a hole that needs a test?

    No. With WAIT_STATES = 1 every access has exactly one wait state, so the item is unreachable in this configuration. It becomes an exclusion whose reason names the parameter, and it is revisited if a configuration with WAIT_STATES = 0 is ever built.

    Next: coverage from many tests and many kinds of item has to become one answer. Regression Merge and the Closure Loop builds it.

    Related topics

    • Up: Coverage & Closure

    • Cover properties and coverage closure

    • Reactive slave agents

    • SystemVerilog: transaction and temporal coverage

    Previous

    142 / 280

    Next: Regression Merge and the Closure Loop

    Up next

    Up nextRegression Merge and the Closure Loop

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Designing a Coverage Model from the Spec
    • Coverage Components and Sampling
    • Bins, Illegal Bins and Ignore Bins
    TVALID held through 3 stalled clocks, then the transferclkTVALIDTREADYTDATAD0c_stall3 match
    TVALID rises with D0 and stays high through three clocks of TREADY low; the beat moves on the fourth edge, and the cover property c_stall3 records one match there. A covergroup sampling completed beats sees only one transfer and a gap.