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 Here6
  • SystemVerilog Language Foundations35
  • Object-Oriented SystemVerilog for Verification35
  • Constraint Randomization — Interview-Critical Deep Dive66
  • SystemVerilog Assertions — Interview-Critical Deep Dive63
  • Functional Coverage in SystemVerilog28
    • Covergroups & Coverpoints
    • Bins: Explicit, Auto, Illegal & Ignore
    • Cross Coverage
    • Coverage Options & Methods
      • option.per_instance & Instance Options
      • Weights, Goals & at_least
      • get_coverage(), sample() & Friends
      • Parameterizing Covergroups
      • Simulator Coverage Control
    • Coverage Closure & Sign-Off
  • Testbench Architecture in SystemVerilog38
  • Advanced Language & Integration30
  • Senior & Interview Preparation34
  • Principal-Level Verification28
Loading lesson

‹Coverage Options & Methods
  1. Topics
  2. SystemVerilog
  3. Functional Coverage in SystemVerilog
  4. Coverage Options & Methods
  5. Simulator Coverage Control

Part 5 · Functional Coverage · Intermediate

Simulator Coverage Control

Coverage databases, $set_coverage_db_name, performance switches, hierarchy exclusion, and the per-seed merge flow.

On this page

    Where coverage actually lives

    Bin hit counts accumulate in simulator memory during the run and are written to a coverage database file at the end (UCDB for Questa, VDB for VCS, and so on — formats differ, concepts do not). The database, not the simulation log, is the artifact: it is what gets merged across seeds, ranked, reported, and archived for sign-off.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // LRM-standard control of the database name from inside the test:
    initial begin
      // make this run's database identifiable by seed
      $set_coverage_db_name($sformatf("cov_seed_%0d", seed));
    end
    
    // Companion system functions (LRM 'Coverage system tasks'):
    //   $load_coverage_db("name")  — load a previously saved database
    //   $get_coverage()            — overall functional coverage %

    Most flows set the database name from the run script rather than the test, but $set_coverage_db_name is the portable in-language hook — and naming databases by seed/test is what makes the merge step automatable.


    The regression merge flow

    No single seed closes a real coverage model. Each seed writes its own database; a merge tool unions the bin hits; the merged database is what closure and hole analysis read. Merging is a union of hits, never an average of percentages.

    Diagram
    REGRESSION MERGE FLOW
    
      test_smoke  seed 1 ──► cov_seed_1.db ──┐
      test_random seed 2 ──► cov_seed_2.db ──┤
      test_random seed 3 ──► cov_seed_3.db ──┼──► MERGE (union of bin hits)
      test_burst  seed 4 ──► cov_seed_4.db ──┤        │
      test_err    seed 5 ──► cov_seed_5.db ──┘        ▼
                                                merged.db
                                                     │
                                      ┌──────────────┼──────────────┐
                                      ▼              ▼              ▼
                                 closure %      hole report    test ranking
                                 vs goals       (empty bins)   (which tests
                                                     │          added bins?)
                                                     ▼
                                           new directed tests / waivers
                                                     │
                                                     ▼
                                           next regression (loop)
    
      seed A: bin X hit 3, bin Y hit 0
      seed B: bin X hit 0, bin Y hit 7   merge → X:3, Y:7  (union, not average)
    • Merge is incremental in most tools — yesterday's merged.db plus today's new seeds is a normal flow.

    • Test ranking reads the merged data to find which runs contributed unique bins — the rest are pruning candidates for the regression list.

    • Per-instance coverage and option.name survive into the database; consistent naming is what makes cross-seed instance merging coherent.


    Performance: turning coverage off where it buys nothing

    Coverage collection costs simulation speed — bin updates on every sample, database writes at exit. Flows therefore disable it where it adds nothing: performance-characterization runs, debug re-runs of a failing seed, and hierarchies outside the verification scope (a vendor PHY model never closes and never needs to).

    Diagram
    COVERAGE ON/OFF DECISION
    
      run type                      functional cov?   why
      ──────────────────────────────────────────────────────────────
      nightly regression seeds      ON                that's the product
      debug re-run of failed seed   OFF               need speed; cov known
      performance benchmark run     OFF               cov skews timing runs
      gate-level sims               usually OFF       different sign-off basis
      vendor IP / PHY hierarchy     EXCLUDED          can't close, don't try
    
      Mechanisms (tool-specific flags, portable concepts):
      - compile/run switches enable coverage kinds (functional, line, toggle)
      - exclusion files remove hierarchies/instances from collection
      - cg.stop()/start() gates one instance from inside the language

    Interview angle: "Walk me through how 300 random seeds become one coverage number" is a flow question disguised as a tool question: per-seed databases named by seed, merge as a union of hits, holes triaged from the merged view, ranking to prune the test list, exclusions documented for out-of-scope hierarchy. Saying "averages" anywhere in that answer is the classic fail — merging unions hits.

    Key takeaways

    • The coverage database file, not the sim log, is the artifact — name it per seed/test so merging is automatable.

    • $set_coverage_db_name / $load_coverage_db / $get_coverage are the LRM-portable hooks; richer control is tool-side.

    • Merge unions bin hits across seeds; closure and hole analysis read only the merged database.

    • Disable or exclude coverage where it cannot pay: debug re-runs, perf runs, vendor hierarchies.

    Common pitfalls

    • Letting every seed write the same default database name — runs overwrite each other and the merge is silently partial.

    • Averaging per-seed percentages instead of merging databases — mathematically wrong, always pessimistic or just false.

    • Collecting coverage on vendor IP hierarchies — permanent un-closeable holes that pollute every report.

    • Re-running a failing seed with coverage on while debugging — paying the slowdown for data you already have.

    Previous

    227 / 363

    Next: Coverage Closure & Sign-Off

    Up next

    Up nextCoverage Closure & Sign-Off

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • option.per_instance & Instance Options
    • Weights, Goals & at_least
    • get_coverage(), sample() & Friends