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
    • What Is UVM?
      • Why UVM Exists: History and Goals
      • What UVM Standardizes (and What You Own)
      • Components vs Objects: Hierarchy, Lifetime, and Phases
      • Objects That Copy, Compare and Print
      • The Stimulus, Check, and Coverage Triangle
      • Your First UVM Testbench
      • The DMA Bench: Full Source, Part 1
      • The DMA Bench: Full Source, Part 2
      • The DMA Bench: Full Source, Part 3
    • Testbench Architecture and Environment Composition
  • 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
  • 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

‹What Is UVM?
  1. Topics
  2. UVM Verification
  3. UVM Foundations
  4. What Is UVM?
  5. The Stimulus, Check, and Coverage Triangle

Part 1 · Foundations · Intermediate

The Stimulus, Check, and Coverage Triangle

Testplan rows and the three legs every feature needs, built on the DMA: the coverage hole a green regression hid, and the weak check high coverage hid.

On this page

    The DMA's regression has been green for a month: smoke, random and truncate tests, hundreds of seeds, zero errors. Then a firmware engineer starts a job with LEN = 0 by accident, and the interrupt never comes on the FPGA prototype. Nobody had ever run that case. The random test's job length is constrained to 1–16, so zero was never generated, and nothing measured that it had not been. Every test passed; the plan had a hole. How do you make holes like that visible before someone else finds them?

    The idea: every feature needs three legs

    A testplan (or verification plan) lists every feature of the design that must be verified, and for each one it answers three questions. How do we make it happen? That is stimulus. How do we know it went right? That is the check. How do we know it actually happened? That is coverage. Leave out any leg and the feature is not verified, just like a three-legged stool with one leg missing is not a stool.

    Each missing leg fails differently. Stimulus without a check is the green-but-checking-nothing test: traffic flows and nothing judges it. A check without coverage is the LEN = 0 story: the scoreboard would have caught a bug, but nothing ever drove the case. Coverage without a check is the most dangerous of all: the coverage report says 100 percent, so everyone believes the feature is verified, but no check would have noticed a wrong result.

    The picture: one row, three legs, one observed stream

    Flow: a testplan row fans out to stimulus, check and coverage; stimulus drives the DUT, and the monitors feed both the check and the coverage.
    A plan row drives stimulus, and both the check and the coverage read the monitors, so they judge and count what the DUT really did, not what the test intended.

    The rule in the bottom half of the picture matters as much as the three legs. Both the check and the coverage read monitored traffic. Coverage sampled from the sequence's items proves only that the stimulus was generated; if the driver dropped an item, or the DUT never accepted it, intent-based coverage still counts it.

    Build it: from spec sentence to UVM parts

    The DMA you will verify lists the design's promises. Here are three of them as testplan rows, with the UVM part that implements each leg. Give every row a stable ID: tests, checks and coverage bins refer to it, and the closure report is organised by it.

    ID

    Feature

    Stimulus

    Check

    Coverage

    F1

    Every word comes out as word ^ KEY

    dma_rand_test: random KEY, LEN, data

    dma_scoreboard predicts from monitored input and KEY

    LEN ranges; KEY all-zeros and all-ones

    F4

    Early TLAST ends the job with ERR

    dma_truncate_test: override cuts the packet

    Test reads STATUS.ERR; scoreboard matches the words that moved

    Cut position: first, middle, last-but-one

    F7

    LEN = 0 finishes at once

    none yet

    none yet

    none yet

    Row F7 is the hook's hole, and writing it down is what exposes it. The plan has a row; the bench has nothing for it. That is the real value of a plan: it turns "we never thought of that" into an empty cell that someone has to fill or explicitly waive.

    The stimulus leg: constraints define the legal space

    The job length comes from dma_job_cfg, whose constraint is len inside {[1:16]}. That constraint is exactly why LEN = 0 was never generated. Constraints should describe what is legal for the DUT, and LEN = 0 is legal; the 1–16 range was a convenience for short simulations, which quietly became a hole. A better version keeps short jobs common and makes zero likely enough to happen:

    design.sv
    1
    2
    3
    4
    // snippet: a job-length distribution that covers the edges
    constraint len_c {
      len dist { 0 := 1, [1:16] := 8, [17:255] := 1 };
    }

    The check leg: predict from what was observed

    The bench's scoreboard is the check for F1, and Your first UVM testbench walks through it. The rule it follows is the one to remember: expected values are predicted from the input-side monitor (and the observed KEY write), and compared against the output-side monitor. Predicting from the output, or from the sequence's intent, makes the check circular or blind to register-path bugs.

    The coverage leg: bins named after plan rows

    A covergroup in a subscriber on the APB monitor's stream measures what lengths the DUT was really programmed with. Each bin carries its plan ID in a comment or name, so the closure report maps back to rows.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // not-run: covergroups are not run in the course's Verilator flow
    class dma_len_cov extends uvm_subscriber #(apb_item);
      `uvm_component_utils(dma_len_cov)
      covergroup cg with function sample(bit [15:0] len);
        cp_len: coverpoint len {
          bins len_zero  = {0};           // F7
          bins len_one   = {1};           // F1 edge
          bins len_short = {[2:16]};      // F1
          bins len_long  = {[17:65535]};  // F1
        }
      endgroup
      function new(string name, uvm_component parent);
        super.new(name, parent);
        cg = new();
      endfunction
      function void write(apb_item t);
        if (t.write && !t.slverr && t.addr == 8'h08) cg.sample(t.data[15:0]);  // LEN writes
      endfunction
    endclass

    Every bin is explicit and bounded, so 100 percent means each named case was seen. An open-ended bin such as {[17:$]} would hide whether the maximum was ever programmed.

    Watch it run: reading a log for coverage

    Before any coverage tool, the log of dma_rand_test already tells you something. From the course's run (the random values differ by simulator and seed):

    Diagram
    UVM_INFO dma_tb.sv(731) @ 0: uvm_test_top [JOB] key=90fdd8fd len=10 irq_en=1
    UVM_INFO dma_tb.sv(731) @ 645000: uvm_test_top [JOB] key=0e564b57 len=1 irq_en=1
    UVM_INFO dma_tb.sv(731) @ 885000: uvm_test_top [JOB] key=5a1793f4 len=3 irq_en=1
    UVM_INFO dma_tb.sv(411) @ 1305000: uvm_test_top.env.sb [SB] 14 words matched, 0 mismatched
    • The check leg worked: 14 words (10 + 1 + 3) were predicted and matched, and nothing was left over.

    • The stimulus leg was narrow: three lengths, all in 1–16, and irq_en was 1 every time (the config's soft constraint). No run with this constraint can ever produce LEN = 0 or a job without the interrupt.

    • The coverage leg would say so: with the covergroup above, len_zero and len_long stay at zero hits however many seeds you run. That is the signal to change the constraint or add a directed test.

    What breaks: high coverage over a weak check

    The opposite failure is quieter and does more damage, and it is a pattern verification leads see again and again. A team inherits a block with 98 percent functional coverage and a long green history. A silicon bug turns up in a feature whose bins were all hit. The scoreboard for that feature compares only the packet count, not the contents, because a previous engineer disabled the data compare while chasing an unrelated mismatch and never turned it back on. Coverage said every case had been exercised; nothing said every case had been checked.

    Two habits prevent it. First, review checks with the same rigour as coverage: for every plan row, someone should be able to point at the line of code that would fail if the feature broke. Second, make checks prove they ran, the way the DMA scoreboard reports its match count and fails when nothing was compared. A disabled check that still prints "0 mismatched" is indistinguishable from a working one.

    Interview

    "How do you know when verification is done?"

    A weak answer: "When we reach 100 percent code and functional coverage." Coverage measures exercise, not correctness, and code coverage says nothing about missing features.

    A strong answer: "When every testplan row has stimulus that hits it, a check that would fail if it broke, and coverage that proves it was hit on the monitored traffic, with any gaps waived explicitly by the designer and architect. I track it per row from merged regressions, and I treat a feature with full coverage and a weak check as not done. Code coverage is a cross-check for features the plan forgot."

    Expect the follow-ups: Where does coverage sample from? (monitors, never the sequence) and What do you do with a hole you can't hit with random stimulus? (constrain toward it, write a directed test, or waive it with a reason).

    Check yourself Coverage sampled in the stream sequence reaches 100 percent for all packet lengths. The output monitor's coverage of the same bins is at 80 percent. What does that tell you?

    Some packets the sequence generated never came out of the DUT in that form: the truncating driver cut them, the DUT stopped early, or the test ended before they drained. The sequence-side number measures intent; the monitor-side number measures reality. Trust the monitor, and find out which lengths went missing and why.

    Check yourself Must every testplan row get its own test?

    No. Each row needs stimulus, a check and coverage, but one random test routinely exercises many rows, and one scoreboard checks many features at once. Directed tests are for rows random stimulus cannot reach efficiently, such as the set-beats-clear race on STATUS.

    Check yourself A coverpoint on LEN has bins big = {[17:$]} and shows 100 percent. Was the maximum length ever programmed?

    Unknown. One hit anywhere from 17 to 65,535 fills the bin. If the maximum matters (and for a 16-bit counter it does: COUNT must not wrap), give it its own bin, such as bins len_max = {16'hFFFF};.

    Next: you have the vocabulary and the plan. Your first UVM testbench puts all of it into one running bench and then breaks it.

    Related topics

    • Functional coverage in UVM

    • Scoreboards

    • Covergroup basics

    Previous

    9 / 280

    Next: Your First UVM Testbench

    Up next

    Up nextYour First UVM Testbench

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Why UVM Exists: History and Goals
    • What UVM Standardizes (and What You Own)
    • Components vs Objects: Hierarchy, Lifetime, and Phases