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

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
  • Part 6: Coverage10
    • What Coverage Measures
    • Code Coverage
    • Functional Coverage Models
    • Crosses and the Combinatorial Explosion
    • Closure Curves
    • Holes and Unreachable Bins
    • Coverage-Driven Stimulus
    • Mutation Testing
    • A Coverage Model for DCRouter
    • Drills: Coverage
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 6: Coverage
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 6: Coverage
  4. Code Coverage

Part 6 · Coverage · Working (W)

Code Coverage

Code coverage is measured automatically from the RTL: lines, branches, conditions, toggles and state-machine states and transitions. It finds code no test executed, cheaply, and it cannot see missing code, wrong values or untested combinations. Each metric, what it misses, and two DCRouter lines where code coverage and functional coverage disagree.

On this page

    Note

    Level: Working (W) · Answers: How do we know we have tested enough?

    Before this: What Coverage Measures

    Code coverage is free: switch it on in the simulator and every regression reports which lines of RTL ran. Teams love it for that reason, and some treat 100% line coverage as the finish line. Then a bug turns up in a line that was executed thousands of times, with a value no test ever drove, or in a feature whose code was never written at all.

    What does code coverage measure, and what can it not see?

    The obvious answer, and why it breaks

    The obvious belief is that executing every line means testing every behaviour. A line can execute with only one value of its inputs; a condition with three terms can be true for only one reason; a bug that is a missing line has no line to cover. Code coverage measures the code that exists, against the stimulus that ran, and nothing about intent.

    The idea: five metrics, five blind spots

    Metric

    Measures

    Misses

    Line (statement)

    Each statement executed

    Values; which branch of a ternary; missing code

    Branch

    Each if/case branch taken, including implicit else

    Combinations of conditions; values inside the branch

    Condition (expression)

    Each term of a condition shown to affect the outcome

    Combinations across separate conditions; timing

    Toggle

    Each signal bit seen rising and falling

    What the toggles meant; sequences

    FSM

    Each state and transition visited

    Paths through several transitions; data in each state

    Two DCRouter lines

    The CTRL write in DCRouter's register block executes on every write to address 0x00:

    design.sv
    // snippet: from dcrouter.sv, the CTRL write
    if (paddr == 8'h00) begin port_en <= pwdata[3:0]; prio_en <= pwdata[4]; lp_en <= pwdata[5]; end

    Lesson 4.10's random hunt writes CTRL at the start of every round, so this line has full line and branch coverage. Yet every write sets port_en to 4'hF: no port is ever disabled, and BUG_23 (a disabled port still accepts traffic) escapes. Toggle coverage would notice: the port_en bits never fall from 1 to 0. Functional coverage notices directly: the bins "CTRL port k disabled" stay empty (lesson 6.9).

    The second line is the error counter's saturation:

    design.sv
    // snippet: from dcrouter.sv, ERR_CNT increment
    err_cnt[k] <= (err_cnt[k] == 8'hFF) ? 8'hFF : err_cnt[k] + 8'd1;

    Line coverage marks it covered after the first error. Whether the == 8'hFF side of the ternary was ever taken is invisible to line coverage and, written as a ternary, often to branch coverage too; expression coverage or a functional bin "ERR_CNT saturated" is needed. That is exactly the case where BUG_15 (wrap instead of saturate) hides.

    What code coverage is good for

    • Finding dead or unreached code: logic no test executed is untested for certain.

    • Finding gaps in the plan: an unexecuted branch is often a feature nobody listed.

    • Cheap, automatic, objective: no model to write.

    • Necessary, not sufficient: unexecuted code is a definite gap; executed code is not a definite pass.

    How real teams use code coverage

    Practice

    Purpose

    Code coverage collected on every regression

    Unexecuted code found early

    Exclusions reviewed and documented

    Unreachable or debug-only code waived with a reason (lesson 6.6)

    Code and functional coverage required together at sign-off

    Each catches what the other misses (Part 14)

    What breaks

    A team (illustrative) reached 100% line and branch coverage on a DMA engine. A customer found that descriptors crossing a 4 KB boundary were split incorrectly. The splitting code had been written for the common case only; the boundary-crossing case had no code at all, so there was nothing for code coverage to report. A functional bin "descriptor crosses 4 KB" written from the specification would have stayed empty.

    Why this matters

    Senior lens. Use code coverage to find what you certainly did not test, never to argue what you did. Its blind spots (values, combinations, missing code) are where functional coverage written from the specification must take over.

    Interview

    Asked: "Why isn't code coverage enough?"

    Weak answer: Because it doesn't cover the testbench.

    Strong answer: It measures execution of the code that exists, not behaviour. A line can run with only one value, a condition can be true for only one reason, combinations across conditions aren't measured, and missing code has nothing to cover. On DCRouter the CTRL write line is fully covered by tests that never disable a port, so a disabled-port bug escapes; toggle coverage hints at it and a functional bin catches it directly. Code coverage is the cheap floor: unexecuted code is a definite gap, executed code proves little.

    Follow-ups: (1) What does toggle coverage add? (2) How do you find missing code?

    Check yourself Which code coverage metric would show that no test disabled a DCRouter port?

    Toggle coverage: the port_en bits would be seen at 1 but never falling to 0.

    Check yourself Why can code coverage never find a missing feature?

    It measures only code that exists; a feature that was never written has no lines, branches or toggles to leave unexecuted.

    Key takeaways

    • Code coverage measures execution of existing RTL: lines, branches, conditions, toggles, FSM states.

    • It misses values, combinations and missing code.

    • Unexecuted code is a definite gap; executed code is not proof of anything.

    Keep going

    Next: Lesson 6.3, Functional Coverage Models. Writing coverage from the plan: points, bins and crosses.

    Related topics

    • Part 6 overview: Coverage

    Previous

    63 / 160

    Next: Functional Coverage Models

    Up next

    Up nextFunctional Coverage Models

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Crosses and the Combinatorial Explosion
    • Closure Curves
    • Holes and Unreachable Bins