Part 5 · Functional Coverage · Intermediate

Weights, Goals & at_least

option.weight in the parent percentage math, option.goal, at_least hit thresholds, and type_option vs option.

option.weight — what the rollup percentage means

A covergroup's percentage is a weighted average of its coverpoints and crosses; a scope's percentage is a weighted average of its covergroups. option.weight (default 1) sets each element's share. Weighting lets the rollup reflect plan priority — and lets you remove bookkeeping coverpoints from the math entirely with weight 0.

systemverilog
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;   // exists only to feed
                                  bins ids[4] = {[0:15]}; } // the cross below
  x_id_resp : cross cp_id, cp_resp { option.weight = 2; }
endgroup

Worked rollup calculation

diagram
ELEMENT      coverage   weight   contribution
  ─────────────────────────────────────────────────
  cp_dir        100%        1      100 × 1 = 100
  cp_resp        50%        3       50 × 3 = 150
  cp_id         100%        0      excluded (w=0)
  x_id_resp      25%        2       25 × 2 =  50
  ─────────────────────────────────────────────────
  weight sum (counted)  = 1 + 3 + 2 = 6
  covergroup %          = (100 + 150 + 50) / 6 = 50.0%

  Unweighted (all w=1) the same numbers give:
  (100 + 50 + 100 + 25) / 4 = 68.8%
  Same simulation. 18.8 points apart. Weights ARE the policy.

option.goal and option.at_least

option.goal (default 100) declares the target percentage at which an element is considered done — reports flag elements below their goal, and get_coverage() style queries can be compared against it. option.at_least (default 1) sets how many hits a bin needs before it counts as covered. One hit can be noise — a single accidental sample landing in a corner cell proves very little about whether the design handles that corner robustly.

systemverilog
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  = {ERR_PARITY};
    bins timeout = {ERR_TIMEOUT};
    bins crc     = {ERR_CRC};
  }
endgroup
// With at_least = 4: parity hit twice -> bin still UNCOVERED.
// Forces stimulus to exercise each error repeatedly, not by luck.

Why 1 hit may be noise

  • A corner cell hit once may have been hit on the only cycle where checking was masked — repeated hits make the evidence statistical.

  • Transition-heavy logic (FIFO full, arbitration loss) often misbehaves only on the Nth occurrence; at_least > 1 forces N occurrences.

  • at_least raises the closure bar — apply it to plan-critical points, not globally, or regressions stretch for little gain.


type_option vs option

Every knob so far was option.* — per-instance state, settable at runtime via the instance handle. type_option.* is the type-scoped sibling: one value shared by all instances of the covergroup type, settable only with constant expressions (elaboration time), controlling the type-level rollup.

diagram
PROPERTY                  option.*              type_option.*
  ───────────────────────────────────────────────────────────────────
  scope                     one instance          the covergroup type
  set when                  runtime OK            elaboration constants
  affects                   instance coverage     type-level coverage
  weight / goal / comment   yes                   yes
  at_least                  yes                   no  (instance only)
  per_instance              yes                   no  (meaningless)
  merge_instances           no                    yes (tool rollup mode)
  typical use               per-port name/weight  type rollup policy
systemverilog
covergroup cg @(posedge clk);
  type_option.weight  = 2;        // this TYPE counts double in scope rollup
  type_option.comment = "vplan section 4";
  option.per_instance = 1;        // instance-level knob

  cp : coverpoint mode { bins m[] = {[0:3]}; }
endgroup

Interview angle: "What is the difference between option and type_option?" is a stock question; the crisp answer is instance-scope-runtime vs type-scope-elaboration. The follow-up that separates candidates: "your group shows 100% inst coverage but the type number is lower — why?" (other instances of the same type are dragging the merged type view down, or type_option.merge_instances policy differs from what you assumed).

Key takeaways

  • weight sets each element's share of the parent percentage; weight 0 removes bookkeeping points from the math.

  • goal declares the done-threshold per element; at_least declares how many hits make a bin trustworthy.

  • One hit is weak evidence for corner behavior — raise at_least on plan-critical coverpoints.

  • option.* is instance-scope and runtime-settable; type_option.* is type-scope and elaboration-constant.

Common pitfalls

  • Leaving every weight at 1 — the rollup says all elements matter equally, which is almost never the plan's view.

  • Setting weight 0 on a coverpoint and forgetting it feeds a cross — the cross still works, but reviewers misread the report.

  • Global at_least = 10 on every point — closure time balloons with no added confidence on non-corner bins.

  • Trying to set type_option from a runtime variable — it requires constant expressions and fails at compile.