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. Weights, Goals & at_least

Part 5 · Functional Coverage · Intermediate

Weights, Goals & at_least

How weights shape the rollup, what goals and hit thresholds mean, and instance versus type scope.

On this page

    Why this matters

    A protocol covergroup read 68.75% with every weight at 1, and the plan-critical response coverpoint sat at 50%. Weighting it 3 made the group read 50%, which was the truth the plan cared about.

    After this lesson you can: work out a weighted rollup, use weight 0 for cross-only helpers, and set goal and at_least where they mean something.

    Weights in the rollup

    A covergroup percentage is a weighted average of its coverpoints and crosses. option.weight sets each element's share, with a default of 1.

    The LRM defines the rollup as the weight-averaged coverage of the items: each element contributes its coverage times its weight, divided by the counted weight sum. Tool reports may add other views, such as bin-count totals.

    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
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup proto_cg @(posedge clk iff txn_done);
      cp_dir  : coverpoint is_write { option.weight = 1;
                                      bins rd = {0}; bins wr = {1}; }
      cp_resp : coverpoint resp     { option.weight = 3;   // plan-critical
                                      bins okay = {0}; bins exok = {1};
                                      bins slverr = {2}; bins decerr = {3}; }
      cp_id   : coverpoint txn_id   { option.weight = 0;   // feeds the cross only
                                      bins ids[4] = {[0:15]}; }
      x_id_resp : cross cp_id, cp_resp { option.weight = 2; }
    endgroup

    Element

    Coverage

    Weight

    Contribution

    cp_dir

    100%

    1

    100 × 1 = 100

    cp_resp

    50%

    3

    50 × 3 = 150

    cp_id

    100%

    0

    Excluded

    x_id_resp

    25%

    2

    25 × 2 = 50

    Covergroup

    1 + 3 + 2 = 6

    (100 + 150 + 50) / 6 = 50%

    With every weight left at 1 the same simulation reads (100 + 50 + 100 + 25) / 4 = 68.75%, 18.75 points higher. Weights are the policy.

    Weight 0 removes bookkeeping points from the math without removing them from crosses. Leaving every weight at 1 says all elements matter equally, which is rarely the plan view.

    Goals and hit thresholds

    option.goal declares the target percentage at which an element counts as done. Its default is 100. A goal only flags reports; it never changes the returned percentage.

    option.at_least sets how many hits a bin needs before it counts as covered. Its default is 1. One hit can be noise, so plan-critical corners ask for repeats.

    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
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup err_cg @(posedge clk iff txn_done);
      option.goal = 90;            // plan accepts 90% on this group
    
      cp_err : coverpoint err_kind {
        option.at_least = 4;       // a bin needs 4 hits to count
        bins parity  = {0};
        bins timeout = {1};
        bins crc     = {2};
      }
    endgroup
    // With at_least = 4, a bin hit twice is still uncovered.
    // Stimulus must exercise each error repeatedly, not by luck.
    • A corner cell hit once may have landed on the only cycle where checking was masked. Repeats make the evidence statistical.

    • Logic that misbehaves on the Nth occurrence needs N occurrences. A threshold above 1 forces them.

    • Thresholds raise the closure bar. Apply them to plan-critical points, not globally.

    Instance scope versus type scope

    Every knob so far was option, which configures one instance. type_option is the type-scoped sibling: one value shared by all instances. In the covergroup body it takes constant expressions only.

    Property

    option

    type_option

    Scope

    One instance

    The covergroup type

    Set where

    Body, or at runtime through an instance handle (except per_instance)

    Body, with constant expressions

    Affects

    Instance coverage

    Type-level coverage

    weight, goal, comment

    Yes

    Yes

    at_least

    Yes

    No, instance only

    per_instance

    Yes, body only

    No, meaningless at type scope

    merge_instances

    No

    Yes: 0 (default) averages instances, 1 takes the union of bins

    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
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup cg @(posedge clk);
      type_option.weight  = 2;        // this type counts double in the scope rollup
      type_option.comment = "vplan section 4";
      option.per_instance = 1;        // instance-level knob
    
      cp : coverpoint mode { bins m[4] = {[0:3]}; }
    endgroup

    Common pitfall

    Setting weight 0 on a coverpoint that feeds a cross confuses reviewers: the cross still samples it. Setting a global at_least of 10 stretches regressions with no added confidence on plain bins. Assigning type_option in the covergroup body from a non-constant expression fails at compile time.

    Interview

    “What is the difference between option and type_option?” wants instance scope versus type scope, and constant-only type_option values in the body. The follow-up: a group shows full instance coverage but a lower type number, which means other instances drag the type-level view down.

    Check yourself Does lowering option.goal raise the coverage percentage?

    No. The goal only marks elements as done in reports. The percentage comes from bin hits, weights, and thresholds.

    Check yourself A coverpoint feeds a cross and has weight 0. Does the cross still work?

    Yes. Weight 0 only removes the coverpoint from the percentage math. The cross still samples it normally.

    Testbench

    The bench builds proto_cg with the bins model, samples four transactions that produce exactly the table above, and reads the group twice: with the lesson's weights and with every weight at 1. Then it applies at_least = 4 and goal = 90 to the error coverpoint.

    Predict first cp_err sees parity 4 times, timeout twice and crc 5 times, with at_least = 4 and goal = 90. What does it read?

    66.67%, 2 of 3 bins: timeout was seen but is still a hole. The report flags the coverpoint as below its goal of 90; the goal does not change the percentage.

    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
    // uses: covmodel
    // The weighted rollup from this lesson, then at_least and goal on an error coverpoint.
    module tb_weights;
      cm_group proto = new("proto_cg");
      cm_point cp_dir = new("cp_dir"), cp_resp = new("cp_resp"), cp_id = new("cp_id"), cp_err = new("cp_err");
      cm_cross x_id_resp;
      initial begin
        real weighted, unweighted;
        cp_dir.add("rd", 0, 0); cp_dir.add("wr", 1, 1);                                     // option.weight = 1
        cp_resp.add("okay", 0, 0); cp_resp.add("exok", 1, 1); cp_resp.add("slverr", 2, 2); cp_resp.add("decerr", 3, 3);
        cp_resp.weight = 3;                                                                 // plan-critical
        cp_id.add_split("ids", 0, 15, 4);  cp_id.weight = 0;                                // feeds the cross only
        x_id_resp = new("x_id_resp", cp_id, cp_resp);  x_id_resp.weight = 2;
        proto.points.push_back(cp_dir); proto.points.push_back(cp_resp); proto.points.push_back(cp_id);
        proto.crosses.push_back(x_id_resp);
        begin
          int dirs[4] = '{0, 1, 0, 1};
          int ids[4]  = '{0, 4, 8, 12};
          int resps[4] = '{0, 1, 0, 1};                                                     // no error responses yet
          foreach (ids[i]) begin cp_dir.sample(dirs[i]); cp_id.sample(ids[i]); cp_resp.sample(resps[i]); x_id_resp.sample(); end
        end
        proto.report();
        weighted = proto.get_coverage();
        cp_dir.weight = 1; cp_resp.weight = 1; cp_id.weight = 1; x_id_resp.weight = 1;      // every weight left at 1
        unweighted = proto.get_coverage();
        $display("  weighted %0.2f%%, unweighted %0.2f%%: same simulation", weighted, unweighted);
        // option.at_least = 4 and option.goal = 90 on the error coverpoint.
        cp_err.add("parity", 0, 0); cp_err.add("timeout", 1, 1); cp_err.add("crc", 2, 2);
        cp_err.at_least = 4; cp_err.goal = 90;
        repeat (4) cp_err.sample(0);
        repeat (2) cp_err.sample(1);                                                        // seen, but not 4 times
        repeat (5) cp_err.sample(2);
        cp_err.report();
        if (weighted != 50.0 || unweighted != 68.75 || cp_err.n_covered() != 2) $fatal(1, "FAIL: rollup math");
        $display("PASS: weighted 50%% against unweighted 68.75%%; timeout hit twice is still a hole at at_least 4");
        $finish;
      end
    endmodule
    Terminal
    covergroup proto_cg: 50.00% (goal 100)
      coverpoint cp_dir: 100.00% (2 of 2 bins)
        rd             2
        wr             2
      coverpoint cp_resp: 50.00% (2 of 4 bins)  below goal 100
        okay           2
        exok           2
        slverr         0     <- hole
        decerr         0     <- hole
      coverpoint cp_id: 100.00% (4 of 4 bins)
    ... 11 more lines ...
        hole: <ids[2], exok>
        hole: <ids[2], slverr>
        hole: <ids[2], decerr>
        hole: <ids[3], okay>
        hole: <ids[3], slverr>
        hole: <ids[3], decerr>
      weighted 50.00%, unweighted 68.75%: same simulation
      coverpoint cp_err: 66.67% (2 of 3 bins)  below goal 90
        parity         4
        timeout        2     <- hole
        crc            5
    PASS: weighted 50% against unweighted 68.75%; timeout hit twice is still a hole at at_least 4

    Remember

    • The rollup is the weight-averaged coverage of the elements.

    • Weight 0 removes a point from the math, not from crosses.

    • goal flags reports; at_least decides what a hit is.

    Rule of thumb: weights are the plan's priorities in numbers.

    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.

    The same four samples read 50% weighted and 68.75% unweighted, and with at_least = 4 a bin hit twice stayed a hole.

    Related topics

    • Parameterizing Covergroups

    • Coverage closure

    Previous

    224 / 363

    Next: get_coverage(), sample() & Friends

    Up next

    Up nextget_coverage(), sample() & Friends

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Parameterizing Covergroups
    • Simulator Coverage Control