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. get_coverage(), sample() & Friends

Part 5 · Functional Coverage · Intermediate

get_coverage(), sample() & Friends

Procedural sampling, coverage queries, start and stop gating, and tests that stop on goal.

On this page

    Why this matters

    A random test ran a fixed 10,000 transactions and reached its goal after 500. Polling coverage every 100 would have cut the run by 95%.

    After this lesson you can: sample with arguments, read coverage at run time, gate collection with stop and start, and write a test that stops on its goal.

    Procedural sampling and queries

    Covergroups are not write-only. Methods trigger sampling procedurally, read the current percentage, and pause collection, which turns coverage into a runtime signal the test can react to.

    Call

    What it does

    cg.sample()

    Triggers a sampling event procedurally

    cg.sample(args) with with function sample

    Samples explicitly passed values instead of a clock event

    cg.get_inst_coverage()

    This instance percentage

    cg.get_coverage()

    Type-level percentage over all instances (average by default, union with merge_instances = 1)

    cg.stop() and cg.start()

    Pause and resume collection on one instance

    cg.set_inst_name(name)

    Sets the instance name procedurally when names are computed

    $get_coverage()

    Overall functional coverage across all covergroups

    The following listing is not run in this course's checks: the open simulators do not count covergroups.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup txn_cg with function sample(bit wr, logic [3:0] len);
      option.per_instance = 1;
      cp_wr  : coverpoint wr  { bins rd = {0}; bins wr_ = {1}; }
      cp_len : coverpoint len { bins s = {1}; bins m = {[2:8]}; bins l = {[9:15]}; }
      x_wl   : cross cp_wr, cp_len;
    endgroup
    
    txn_cg cg = new();
    
    task collect(bit wr, logic [3:0] len);
      cg.sample(wr, len);                    // explicit, argument-passing sample
      $display("inst=%0.1f type=%0.1f",
               cg.get_inst_coverage(), cg.get_coverage());
    endtask

    Sampling both on a clock event and procedurally double counts. Pick one sampling style per covergroup.

    Tests that stop on goal

    The highest-value use of the query API is a test that watches its own coverage. The classic pattern caps random stimulus by achievement instead of by a fixed transaction count.

    The following listing is not run in this course's checks: the open simulators do not count covergroups.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    // Run until the coverage goal or the transaction budget, whichever first
    task run_random_phase(int max_txns = 10000);
      int n = 0;
      while (n < max_txns) begin
        drive_random_txn();                    // stimulus with cg.sample inside
        n++;
        if ((n % 100) == 0) begin              // poll every 100 txns, not every txn
          if (cg.get_inst_coverage() >= cg.option.goal) begin
            $display("goal hit after %0d txns, stopping random phase", n);
            break;
          end
        end
      end
      if (cg.get_inst_coverage() < cg.option.goal)
        $display("budget spent below goal, holes need directed tests");
    endtask
    • Poll on a decimated schedule. Each query walks bin state, so per-transaction polling slows hot paths.

    • set_inst_name does procedurally what option.name does declaratively.

    • $get_coverage is coarse but handy for a single end-of-test gate.

    • Per-coverpoint queries let adaptive tests steer at finer grain than the whole group.

    Gating collection windows

    Reset and error-recovery traffic is not plan traffic. Gating keeps closure honest by excluding those windows from collection.

    The following listing is not run in this course's checks: the open simulators do not count covergroups.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    bit collecting = 0;
    
    initial begin
      cg.stop();                       // nothing counts yet
      wait (rst_n == 1'b1);
      repeat (10) @(posedge clk);       // settle cycles
      cg.start();                      // begin honest collection
      collecting = 1'b1;
    end
    
    // Synchronous recovery gate: suspect traffic never counts
    always @(posedge clk) begin
      if (fatal && collecting) begin
        cg.stop();
        collecting = 1'b0;
      end
      else if (recovered && !collecting) begin
        cg.start();
        collecting = 1'b1;
      end
    end

    Samples during a stopped window are simply not counted. A stop without a matching start silently collects nothing for the rest of the test.

    Common pitfall

    Polling get_coverage every transaction slows simulation on hot paths. Treating an in-test 100% as closure confuses one seed view with the merged regression. Forgetting start after a stop loses the rest of the test.

    Interview

    “How would you make a random test smarter about coverage?” expects the poll-and-stop pattern plus its caveats: poll cheaply, and never confuse a single-run number with merged closure. The sharper follow-up asks when get_coverage and get_inst_coverage differ: only with multiple instances.

    Check yourself When do get_coverage and get_inst_coverage return different numbers?

    When several instances exist. One reads the type-level view and the other reads a single instance.

    Check yourself A test reaches 100% and stops itself. Is coverage closed?

    No. That is one seed view. Sign-off closure reads the merged regression database, which the coverage-closure topic owns.

    Testbench

    The bench builds txn_cg with the bins model and a sample(wr, len) function. It stops collection over 20 reset-time samples, starts it, then runs random traffic that polls the coverage every 100 transactions and stops at the goal.

    Predict first What does the group read after the 20 stopped samples, and when can the random phase stop?

    0%: a stopped instance counts nothing. The random phase can only stop on a poll, so at a multiple of 100 transactions; on this stream it stops after 500, once the rare write and single-beat cells are all hit.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // uses: covmodel
    // sample() with arguments, stop() and start() around reset, and a random phase that stops on its goal.
    module tb_methods;
      cm_group txn = new("txn_cg");
      cm_point cp_wr = new("cp_wr"), cp_len = new("cp_len");
      cm_cross x_wl;
      int unsigned r = 32'd7;
      function int unsigned rnd(int unsigned n);         // a fixed generator, so every simulator sees the same stream
        r = r * 32'd1103515245 + 32'd12345;
        return (r >> 8) % n;
      endfunction
      // covergroup txn_cg with function sample(bit wr, logic [3:0] len);
      function void sample(bit wr, int len);
        cp_wr.sample(wr); cp_len.sample(len); x_wl.sample();
      endfunction
      function void stop();  cp_wr.enabled = 0; cp_len.enabled = 0; endfunction
      function void start(); cp_wr.enabled = 1; cp_len.enabled = 1; endfunction
      initial begin
        int n = 0;
        cp_wr.add("rd", 0, 0); cp_wr.add("wr_", 1, 1);
        cp_len.add("s", 1, 1); cp_len.add("m", 2, 8); cp_len.add("l", 9, 15);
        x_wl = new("x_wl", cp_wr, cp_len);
        txn.points.push_back(cp_wr); txn.points.push_back(cp_len); txn.crosses.push_back(x_wl);
        // Reset traffic: stop() before it, start() after the settle cycles.
        stop();
        repeat (20) sample(rnd(2) == 1, 1 + rnd(15));
        $display("  after 20 samples while stopped: %0.2f%%", txn.get_coverage());
        if (txn.get_coverage() != 0.0) $fatal(1, "FAIL: a stopped covergroup counted");
        start();
        // Run until the goal or the budget, whichever comes first, polling every 100 transactions.
        while (n < 10000) begin
          sample(rnd(4) == 0, rnd(8) == 0 ? 1 + rnd(15) : 2 + rnd(7));   // writes and single beats are rare
          n++;
          if (n % 100 == 0 && txn.get_coverage() >= txn.goal) break;
        end
        $display("  goal reached after %0d transactions; one poll per 100", n);
        txn.report();
        if (txn.get_coverage() != 100.0 || n % 100 != 0 || n >= 10000) $fatal(1, "FAIL: the adaptive loop");
        $display("PASS: the stopped window counted nothing, and the random phase stopped itself at the goal");
        $finish;
      end
    endmodule
    Terminal
      after 20 samples while stopped: 0.00%
      goal reached after 500 transactions; one poll per 100
    covergroup txn_cg: 100.00% (goal 100)
      coverpoint cp_wr: 100.00% (2 of 2 bins)
        rd             378
        wr_            122
      coverpoint cp_len: 100.00% (3 of 3 bins)
        s              5
        m              465
        l              30
      cross x_wl: 100.00% (6 of 6 bins)
    PASS: the stopped window counted nothing, and the random phase stopped itself at the goal

    Remember

    • Pick one sampling style per covergroup.

    • stop() samples count nothing until start().

    • Poll coverage on a schedule, not every transaction.

    Rule of thumb: an in-test 100% is one seed, not closure.

    How this was checked

    Counted with the course's bins model (rich/coverage/cov_model.sv, listed on the Coverage hub) on Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator 5.048 parses covergroups with a COVERIGN warning and ignores their bins, and Icarus 13 has none, so each covergroup listing is marked not run and its bench counts the same bins with the model.

    20 samples while stopped counted nothing, and the random phase stopped itself at the goal after 500 transactions, on a poll.

    Related topics

    • Weights, Goals and at_least

    • Coverage closure

    Previous

    225 / 363

    Next: Parameterizing Covergroups

    Up next

    Up nextParameterizing Covergroups

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • option.per_instance & Instance Options
    • Simulator Coverage Control