Skip to content

SystemVerilog · Module 11

Coverage Options

The full vocabulary of option.* and type_option.* settings — at_least, weight, goal, comment, per_instance, auto_bin_max, illegal_bins_error, name — that tune coverage models toward sign-off. The instance-vs-type-scope rule, the option-precedence hierarchy, and the practical patterns that separate working coverage from hand-waving.

Module 11 · Page 11.8

Why Coverage Has Options at All

Modules 11.2 through 11.7 built the structural vocabulary — covergroups, coverpoints, bins, transitions, crosses. The measurement those structures produce is governed by a separate vocabulary: the options. Options answer questions the structural model leaves implicit:

  • How many hits does a bin need to be "covered"? (at_least)
  • How much does this coverpoint contribute to the covergroup's headline percentage? (weight)
  • What percentage are we declaring "sign-off ready"? (goal)
  • What is this coverpoint for — which verification-plan row does it implement? (comment)
  • Are we tracking per-instance, or merging across every instance of this type? (per_instance)
  • How many automatic bins does an unbinned coverpoint produce? (auto_bin_max)
  • What happens when an illegal bin is hit — warning or fatal? (illegal_bins_error)

Every one of these settings affects the number on the dashboard, the audit trail behind the number, and the regression cost to close the model. Treating options as "decorations to add later" is one of the most common reasons coverage models drift away from the verification plan they're meant to enforce.

The Full Option Vocabulary

Six options carry most of the day-to-day work; four more handle specialised cases. Production environments use the first six on most coverpoints and reach for the rest when the verification plan demands them.

OptionScopeDefaultWhat it controls
at_leastbin / coverpoint / cross / covergroup1Hits required per bin for "covered"
weightcoverpoint / cross1Contribution to covergroup headline percentage
goalcoverpoint / cross / covergroup100Target percentage that defines "done"
commentevery level""Engineer-authored audit trail string
per_instancecovergroup0 (off)Track per-instance separately in reports
auto_bin_maxcoverpoint / covergroup64Maximum number of automatic bins
namecoverpoint / cross / cg-instancetool-generatedReport label
cross_num_print_missingcross0How many uncovered combinations to list in reports
illegal_bins_errorcovergroup (type_option)tool-defaultForce fatal on illegal-bin hit
merge_instancescovergroup (type_option)tool-defaultHow instance results merge at sign-off

The rest of this page walks each option's mechanics — the ones whose subtlety has bitten enough teams to be worth a detailed treatment.

at_least — Raising the Per-Bin Closure Bar

option.at_least = N raises the number of hits a bin needs before it is "covered" from the default 1. Useful for bins where a single accidental hit lies about real coverage: rare race-condition windows, single-event upsets, transient error paths.

SystemVerilog — at_least at multiple scopes
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg;
    // ── Per-coverpoint default — every bin in cp_a needs 5 hits ────
    cp_a: coverpoint a {
        option.at_least = 5;
        bins low  = {[0:127]};
        bins high = {[128:255]};
    }
 
    // ── Per-bin override — one bin needs more hits than its siblings
    cp_b: coverpoint b {
        option.at_least = 3;        // default for bins in cp_b
        bins routine    = {[0:127]};
        bins critical[] = {[200:204]} with (1) {
            // (per-bin scope is rare in practice; usually achieved via
            // a separate coverpoint with a higher at_least)
        }
    }
 
    // ── Per-cross — every cross combination needs 10 hits ──────────
    cr_a_b: cross cp_a, cp_b {
        option.at_least = 10;
    }
endgroup

The semantics rule. at_least is a closure threshold, not a minimum-hit assertion. A bin with 4 hits when at_least = 5 is "not covered yet" — the closure number for that bin is 4/5 = 80%. The bin is counted toward the closure denominator regardless; only the numerator depends on whether the threshold has been crossed.

Practical values:

  • at_least = 1 (default) — for most bins. A single hit is enough evidence the scenario was exercised.
  • at_least = 5..10 — for critical bins where a single hit could be accidental. Reset-recovery transitions, race-condition windows, fault-injection paths.
  • at_least = 100..1000 — for stress scenarios where statistical coverage matters more than presence. Performance-corner bins, distribution-shape bins.

Setting at_least higher than 1 changes the regression cost: a 100-bin coverpoint at at_least = 10 needs 1,000 hits distributed across the bins (best case) — roughly 10x the regression length of the default. Use the option deliberately; document the reason in comment.

weight — Shaping the Headline Percentage

option.weight = W controls how much a coverpoint or cross contributes to the covergroup's overall coverage percentage. The covergroup headline is a weighted average:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup_pct = Σ(item_pct × item_weight) / Σ(item_weight)

Default weight = 1 for every item. Higher weights pull the headline percentage toward (or away from) the weighted item's percentage more strongly.

SystemVerilog — weight at coverpoint and cross scope
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg;
    // ── Critical coverpoint — pulls the headline hard ──────────────
    cp_critical: coverpoint critical_field {
        option.weight = 10;          // 10× influence
        bins legal[] = {[0:7]};
    }
 
    // ── Routine coverpoint — default contribution ─────────────────
    cp_routine: coverpoint routine_field {
        option.weight = 1;
        bins low  = {[0:127]};
        bins high = {[128:255]};
    }
 
    // ── A cross is weighted independently ─────────────────────────
    cr_main: cross cp_critical, cp_routine {
        option.weight = 5;           // 5× — pulls toward cross gap
    }
endgroup
 
// If after a regression:
//   cp_critical  =  80%  (weight 10)
//   cp_routine   = 100%  (weight  1)
//   cr_main      =  60%  (weight  5)
//
// Covergroup headline =
//   (80 × 10 + 100 × 1 + 60 × 5) / (10 + 1 + 5)
//   = (800 + 100 + 300) / 16
//   = 1200 / 16
//   = 75.0%
//
// Default (all weight=1):
//   (80 + 100 + 60) / 3 = 80.0%
//
// The weighted version is *lower* — the critical gap is amplified
// in the headline, which is precisely the intent.

The rule that follows. Weight is the verification-plan's priority expressed as math. A coverpoint the spec flags as critical should have a higher weight than one the spec flags as routine; the headline percentage will then reflect "the critical gap" rather than "the average gap." For a covergroup with thirty coverpoints, one critical bin at 50% buried in twenty-nine routine bins at 100% would look healthy at 98% under default weighting — and look unhealthy at 65% under priority-weighted scoring. The dashboard is now telling the truth about what matters.

Setting weight = 0 is legal and means "this coverpoint contributes nothing to the headline percentage." Useful for diagnostic coverpoints that you want to see in the report but not have affect the closure number — for example, a coverage point that tracks "did this rare regression branch fire" purely as a probe, not as a sign-off scenario.

goal — Declaring "Done" at Less Than 100%

option.goal = N declares the target percentage for the item. The default is 100 (every bin must be covered). Setting goal = 90 declares "we sign off when 90% of bins are hit"; the report's "achievement" column compares actual coverage against this number, not against 100%.

SystemVerilog — goal at coverpoint and covergroup scope
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg;
    // ── Mandatory — every bin must be hit ──────────────────────────
    cp_mandatory: coverpoint critical {
        option.goal = 100;
        bins must_hit[] = {[0:15]};
    }
 
    // ── Aspirational — we declare done at 80% ──────────────────────
    cp_aspirational: coverpoint stress_corner {
        option.goal = 80;
        option.comment = "stress-corner bins are hard to close; sign-off "
                          + "policy: 80% of bins exercised (vplan §12.3)";
        bins corners[] = {[200:299]};
    }
 
    // ── Covergroup-level goal — overall closure target ─────────────
    option.goal = 95;
endgroup

Use cases for goal < 100:

  • Stress / corner bins that are statistically hard to reach but partial coverage is acceptable per the spec.
  • Aspirational coverpoints that document scenarios the spec encourages but doesn't mandate.
  • Hardware-limited unreachable bins — when a fraction of declared bins are truly unreachable in the current silicon revision (and you don't want to use ignore_bins because the unreachability is hardware-revision-dependent).

The misuse case is the same as with ignore_bins and weight: lowering the goal to make the number look better. Any goal < 100 setting needs a documented architectural reason — code review rejects unexplained downward goals.

comment — The Audit Trail Anchor

option.comment is a string attached to any covergroup, coverpoint, cross, or bin. It appears in the coverage report alongside the item. The convention production environments enforce: every coverpoint, cross, and covergroup carries a comment that cites the verification-plan rows it implements.

SystemVerilog — comment as a verification-plan citation
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg_apb_basic;
    option.comment = "vplan §4.1–§4.5 — APB basic transaction coverage";
 
    cp_paddr: coverpoint paddr {
        option.comment = "vplan §4.1 — CSR/RAM/MMIO region coverage";
        bins csr  = {[32'h0000_0000:32'h0000_00FF]};
        bins ram  = {[32'h1000_0000:32'h1FFF_FFFF]};
        bins mmio = {[32'h2000_0000:32'h2FFF_FFFF]};
    }
 
    cp_pwrite: coverpoint pwrite {
        option.comment = "vplan §4.2 — read/write split";
        bins read  = {1'b0};
        bins write = {1'b1};
    }
 
    cr_addr_dir: cross cp_paddr, cp_pwrite {
        option.comment = "vplan §4.3 — every region × direction combination";
    }
endgroup

Why this matters. At sign-off, an external auditor (verification lead, IP integrator, certification body) reads the coverage report and asks "which verification-plan row does each item cover?" Without comment citations the answer requires the auditor to know the engineer who wrote the model. With citations the answer is in the report — the audit trail is self-contained and survives team turnover.

For DO-254 / ARP4754A / ISO 26262 flows, the citation pattern is required — the traceability matrix that maps spec rows to verification evidence is built directly from comment strings. The cost of the convention is one string per item; the certification value is project-critical.

per_instance — Instance vs Type Visibility

option.per_instance = 1 tells the simulator to preserve per-instance coverage data and report each instance separately, alongside the merged type-level number. The default 0 merges every instance's coverage into one type-level percentage with no per-instance breakdown in the report.

SystemVerilog — per_instance for multi-port environments
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class dual_port_mem;
    covergroup cg_port;
        option.per_instance = 1;                  // ← preserves per-instance view
        option.comment = "vplan §8 — per-port memory transaction coverage";
 
        cp_addr: coverpoint addr;
        cp_dir:  coverpoint write;
    endgroup
 
    cg_port port_a_cov;
    cg_port port_b_cov;
 
    function new();
        port_a_cov = new();
        port_b_cov = new();
        port_a_cov.set_inst_name("port_a");      // readable instance label
        port_b_cov.set_inst_name("port_b");
    endfunction
endclass

The report you get with per_instance = 1:

Report — per-instance breakdown preserved
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Group  cg_port [port_a]                        coverage   100.0%
  cp_addr                                       100.0%   ( 412 hits )
  cp_dir                                        100.0%   ( 412 hits )
 
Group  cg_port [port_b]                        coverage    62.5%
  cp_addr                                        50.0%   (   1/2 bins )
  cp_dir                                        100.0%   ( 198 hits )
 
Group  cg_port (merged)                        coverage    81.3%
  cp_addr                                        75.0%   ( merged 1.5/2 effective )
  cp_dir                                        100.0%   ( 610 hits )

Without per_instance = 1, the per-port breakdown disappears entirely from the report — the simulator merges port_a and port_b into a single covergroup-type number. For multi-instance environments (dual-port memories, multi-channel DMAs, N-way bus monitors), this is almost always the wrong setting.

auto_bin_max — Tuning the Default Bin Partition

Covered in detail in Module 11.3 (Automatic Bins). The summary: option.auto_bin_max = N controls how many automatic bins are generated for an unbinned coverpoint. Default 64. Lower values produce coarser auto bins (one bin per ~N consecutive values for wide types); higher values produce finer ones.

The recurring advice from 11.3 applies here: tuning auto_bin_max is almost never the right answer. If the auto-bin partition doesn't match the verification plan, the right fix is explicit bins declarations matched to the plan. Tuning auto_bin_max to make the dashboard number look better is the auto-bin equivalent of fudging weights — the metric changed, the verification work did not.

The one legitimate use of auto_bin_max is for early-stage smoke-test covergroups where you want fewer-but-still-useful auto bins on wide fields. For sign-off models, set it once at the covergroup or type scope and document the value in comment; any per-coverpoint override needs a stronger justification.

illegal_bins_error — Forcing Fatal Severity

type_option.illegal_bins_error = 1 forces fatal severity on any illegal_bins hit within instances of the covergroup type. The default is tool-specific (typically warning); the setting overrides the tool default and applies uniformly.

SystemVerilog — forcing fatal illegal-bin severity at type scope
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg_critical_contract;
    type_option.illegal_bins_error = 1;
    type_option.comment = "Contract violations are tape-out blockers — "
                           + "fatal severity enforced at type scope";
 
    cp_state: coverpoint state {
        bins legal = {IDLE, ACTIVE, DONE};
        illegal_bins encoding_err = {2'b11};
    }
endgroup

Why type scope, not instance scope. The illegal-bin contract is a design property — if it applies to one instance of the covergroup type, it applies to all of them. Setting illegal_bins_error at type scope expresses this directly. Setting it at instance scope is legal but rare; an instance-specific severity override usually signals confused thinking.

For CI regression flows, illegal_bins_error = 1 is the right default — a violation should stop the regression at the failing simulation, not be lost in a nightly's warning torrent. For interactive debug runs, the default warning severity is sometimes more useful (the simulation continues past the violation and the engineer can inspect what happened next). Most production environments keep the type-option fatal and override per-run via the simulator CLI when interactive debug is needed.

name and set_inst_name() — Labels for the Report

The default report uses tool-generated names for covergroup instances (cg_port_1, cg_port_2). Two settings make them readable:

SystemVerilog — naming covergroup instances for the report
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg;
    // ── option.name at type scope — affects every instance ────────
    option.name = "Memory Controller Coverage";
 
    cp_state: coverpoint state;
endgroup
 
class dma_engine;
    cg channel_a, channel_b;
 
    function new();
        channel_a = new();
        channel_a.set_inst_name("dma.channel_a");  // ← per-instance label
 
        channel_b = new();
        channel_b.set_inst_name("dma.channel_b");
    endfunction
endclass
 
// Reports now display:
//   "Memory Controller Coverage" [dma.channel_a]
//   "Memory Controller Coverage" [dma.channel_b]
//
// Much easier to navigate than:
//   cg_1 [unnamed]
//   cg_2 [unnamed]

For any environment with more than two instances of the same covergroup type, set_inst_name() calls in the constructor are part of basic discipline — the report becomes navigable, the verification audit becomes traceable, and the dashboard's per-instance breakdown (with per_instance = 1) becomes useful.

The Option-Precedence Hierarchy

When multiple options conflict — same option set at multiple scopes — the simulator resolves the precedence top-down:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1. Per-bin override         (rare; bin-scope option syntax)
2. Per-coverpoint / per-cross option
3. Per-covergroup-instance option
4. Per-covergroup-type type_option
5. Tool default

The most specific scope wins. A cp_addr.option.weight = 10 overrides the covergroup's option.weight = 5, which overrides the type's type_option.weight = 1, which overrides the tool default 1.

The practical consequence: for any option you set at type scope, expect instance and coverpoint overrides to drift over time. The audit pattern that prevents drift is "every option set at coverpoint or instance scope cites its reason in option.comment — without the citation, the override is presumed accidental." Code review enforces it on PR.

A Complete Working Example — Every Major Option in Context

The pattern below shows a production-shaped coverage model with the option vocabulary applied as the discipline requires.

SystemVerilog — every major coverage option in one covergroup
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
typedef enum bit [2:0] {
    CMD_READ = 3'b000, CMD_WRITE = 3'b001, CMD_RFR = 3'b010
} cmd_e;
 
class mem_txn;
    rand cmd_e       cmd;
    rand bit [31:0]  addr;
    rand bit [ 3:0]  burst_len;
 
    covergroup cg_mem;
        // ── Type-level options (apply to every instance) ──────────
        type_option.illegal_bins_error = 1;
        type_option.comment            = "vplan §6 — memory controller coverage type";
 
        // ── Instance-level options ────────────────────────────────
        option.name        = "Memory Controller Transaction";
        option.per_instance = 1;
        option.comment     = "vplan §6.1–§6.5 — per-port transaction coverage";
        option.goal        = 100;
 
        // ── Command coverage — critical, weight 10 ─────────────────
        cp_cmd: coverpoint cmd {
            option.weight   = 10;
            option.at_least = 5;
            option.comment  = "vplan §6.1 — every legal command "
                               + "exercised at least 5 times";
            bins rd  = {CMD_READ};
            bins wr  = {CMD_WRITE};
            bins rfr = {CMD_RFR};
            illegal_bins reserved = {[3'b011 : 3'b111]};
        }
 
        // ── Address region coverage — routine, default weight ──────
        cp_addr: coverpoint addr {
            option.weight  = 1;
            option.comment = "vplan §6.2 — address region partition";
            bins csr  = {[32'h0000_0000 : 32'h0000_00FF]};
            bins ram  = {[32'h1000_0000 : 32'h1FFF_FFFF]};
            bins mmio = {[32'h2000_0000 : 32'h2FFF_FFFF]};
            ignore_bins reserved_region = {[32'hF000_0000 : 32'hFFFF_FFFF]};
        }
 
        // ── Burst length coverage — aspirational goal of 80% ───────
        cp_burst: coverpoint burst_len {
            option.weight  = 1;
            option.goal    = 80;
            option.comment = "vplan §6.3 — burst length distribution; "
                              + "stress-corner bins acceptable at 80%";
            bins len[] = {[1:16]};
        }
 
        // ── Critical cross — weight 5, requires 10 hits per bin ───
        cr_cmd_addr: cross cp_cmd, cp_addr {
            option.weight   = 5;
            option.at_least = 10;
            option.comment  = "vplan §6.4 — command × region combinations; "
                               + "critical scenario per § 6.4.1";
        }
    endgroup
 
    function new();
        cg_mem = new();
    endfunction
 
    function void post_randomize();
        cg_mem.sample();
    endfunction
endclass
 
// In the testbench:
class env;
    mem_txn port_a, port_b;
    function new();
        port_a = new();  port_a.cg_mem.set_inst_name("port_a");
        port_b = new();  port_b.cg_mem.set_inst_name("port_b");
    endfunction
endclass

What this code does. Models a memory-controller transaction with three coverpoints (command, address, burst length) and one cross (cmd × addr-region). Each item carries the option vocabulary the discipline requires: weight reflects verification-plan priority; at_least raises the closure bar on critical items; goal allows aspirational closure on stress-corner bins; comment cites every item's verification-plan row; per_instance = 1 keeps per-port visibility in the report; illegal_bins_error = 1 at type scope makes reserved-command violations fatal. Two transaction instances (port_a, port_b) get readable instance names via set_inst_name().

Expected report shape (truncated, sign-off-style).

Memory Controller Transaction — per-instance report
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Group  Memory Controller Transaction [port_a]   coverage   85.7%
  cp_cmd      (weight 10, at_least 5)            100.0%   ( all bins ≥5 hits )
  cp_addr     (weight  1)                        100.0%   ( all regions hit )
  cp_burst    (weight  1, goal 80)               100.0%   ( 13/16 bins, ≥80% )
  cr_cmd_addr (weight  5, at_least 10)            72.2%   ( 13/18 ≥10 hits )
 
Group  Memory Controller Transaction [port_b]   coverage   91.3%
  ... per-port breakdown ...
 
Group  Memory Controller Transaction (merged)   coverage   88.5%
  ... type-level merged numbers ...

The report makes the verification-plan citations, the priority weighting, and the per-instance breakdown all visible — an external auditor can read the report and verify the spec rows are covered without needing the engineer's local context.

Debug Lab — "The Headline Coverage Dropped After a Refactor"

A failure mode that catches every team that touches an option configuration in a multi-engineer environment.

Buggy code
SystemVerilog — refactor changed the weighted-average inadvertently
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Before refactor ───────────────────────────────────────────────
covergroup cg;
    cp_a: coverpoint a;            // weight 1 (default)
    cp_b: coverpoint b;            // weight 1 (default)
    cp_critical: coverpoint c {
        option.weight = 10;        // explicit priority weighting
        bins must_hit[] = {[0:7]};
    }
endgroup
 
// Headline = (cp_a × 1 + cp_b × 1 + cp_critical × 10) / 12
 
// ── Refactor: someone "cleaned up" by removing the explicit weight,
// thinking the default would be fine ──────────────────────────────
covergroup cg_after;
    cp_a: coverpoint a;
    cp_b: coverpoint b;
    cp_critical: coverpoint c {
        // option.weight = 10;     // ← removed during "tidy-up"
        bins must_hit[] = {[0:7]};
    }
endgroup
 
// Headline = (cp_a × 1 + cp_b × 1 + cp_critical × 1) / 3
// The critical coverpoint now contributes only 1/3 of its previous weight.
Symptom

The covergroup compiles. The regression runs without errors. The dashboard's headline coverage moves — and the direction is non-obvious. If cp_critical was lower than the average, the headline goes up (the critical gap is no longer amplified, so the average looks better). If cp_critical was higher than the average, the headline goes down. The team spends days investigating "what regressed in the design or testbench" when the regression is in the measurement, not in the design.

Root cause

The refactor changed an option without changing the structural definition or the closure rule — only the measurement weight changed. The headline is a weighted average; removing a high weight from one coverpoint shifts the average toward the unweighted items. The simulator does exactly what it was told; nothing is broken in the code review sense. But the dashboard is now reporting a different metric than it was before the refactor, and the verification-plan priorities are no longer reflected in the headline.

The structural mistake is treating options as "decoration" that can be removed without consequence during cleanup. Options shape the metric the dashboard reports — removing them is changing the metric.

Fix

Three-part fix. First, restore the priority weights. Second, document why each non-default weight exists, so future refactors know the option carries semantic meaning. Third, add a regression-time check that flags non-default option settings in PR diffs.

SystemVerilog — weights restored with documentation
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup cg;
    cp_a: coverpoint a {
        option.comment = "vplan §2.1 — routine coverage, default weight";
    }
    cp_b: coverpoint b {
        option.comment = "vplan §2.2 — routine coverage, default weight";
    }
    cp_critical: coverpoint c {
        option.weight  = 10;
        option.comment = "vplan §2.3 — critical scenario; weight 10 "
                           + "reflects sign-off priority over §2.1–§2.2";
        bins must_hit[] = {[0:7]};
    }
endgroup

The discipline rule that prevents recurrence: every non-default option setting cites its reason in option.comment. PR review enforces it; a non-default option without a citation is presumed accidental and the reviewer requests the citation. Some teams go further and run a static-analysis check that fails any PR removing or modifying an option.* line without a corresponding edit to the cited option.comment.

The complementary discipline: dashboard regressions (headline coverage dropping or rising more than ~5% across consecutive regressions) trigger a coverage-model audit, not a design audit, as the first step. The model is the more likely culprit when the measurement changes between PRs.

Industry Context — Where Options Carry Their Weight

  • DO-254 / ARP4754A / ISO 26262 flows. Every option.comment citation is an entry in the traceability matrix between spec rows and verification evidence. Certification bodies read the coverage report alongside the spec and verify the citations match. Coverage models without citations fail the trace-evidence audit.
  • IP vendor sign-off contracts. When verification IP ships to integrators (ARM AMBA VIP, Synopsys VC VIP, Cadence VIPC, Mentor Questa VIP), the coverage report is part of the deliverable. The option.comment strings, option.weight priorities, and option.goal thresholds form the contract: "these are the scenarios we have verified, these are their priorities, these are the closure rules."
  • Multi-instance environments. SoC PMU coverage across multiple power domains; cache coverage across N ways; DMA coverage across multiple channels. option.per_instance = 1 + set_inst_name() is the structural pattern that makes per-domain / per-way / per-channel coverage navigable. Without these settings the report collapses into a single aggregate number that hides domain-specific gaps.
  • Continuous-integration regression gating. type_option.illegal_bins_error = 1 forces fatal severity, which means an illegal-bin hit stops the failing simulation in CI rather than being lost in the warning torrent. Production CI flows gate on "no fatal coverage violations" as a separate signal from "tests pass" — both are required for a green build.
  • Statistical-coverage scenarios. Stress-corner verification, fault-injection campaigns, and security/safety coverage all use option.at_least to raise the bar from "happened once" to "happened robustly." at_least = 100 or at_least = 1000 is common in safety-critical flows where a single hit is insufficient evidence.

The structural fact behind these uses: options are not metadata. They are the verification-plan-to-coverage interface. Every non-default option setting is an architectural decision; every default option setting is also an architectural decision (a deliberate choice to use the tool default). Production coverage models treat both as semantically meaningful and audit them as part of the verification-plan review.

Interview Q&A — Ten Questions You Will Be Asked

option.* applies to a specific instance of a covergroup — different instances can carry different values. type_option.* applies to the covergroup type — set once at type scope, every instance inherits it. The most specific scope wins when both are set. For per-instance differences (per-port labels, per-domain weights), use option; for type-wide rules (illegal severity, default auto_bin_max), use type_option.

Coding Guidelines — Seven Rules for Coverage-Option Discipline

  1. Every non-default option setting cites its reason in option.comment. The citation links the option to a verification-plan row, a spec-priority decision, or an architectural constraint. Without the citation, the option is presumed accidental and code review rejects it.
  2. Pick scope deliberately. Per-instance differences → option. Type-wide rules → type_option. Document the choice in a header comment on the covergroup so the reader knows where to find each setting.
  3. Weight reflects spec priority, not number-massaging. Critical coverpoints carry higher weight; routine coverpoints stay at default. Any weight change in a PR requires a spec-priority justification.
  4. Goal at less than 100% needs an architectural reason. Stress corners, hardware-limited unreachable bins, aspirational scenarios — these are legitimate. Lowering goal to make a dashboard look better is the same anti-pattern as fudging weights.
  5. per_instance = 1 for any covergroup with multiple instances. Otherwise the per-instance breakdown is lost from the report and from cross-regression merging.
  6. type_option.illegal_bins_error = 1 for CI-regression covergroups. Fatal severity surfaces violations in the failing simulation; warning severity loses them in the nightly torrent.
  7. Treat option lines as semantic, not cosmetic. Removing or modifying an option changes the metric the dashboard reports, even when the structural model is identical. Refactors should preserve option settings; cleanup PRs should not touch options without a documented reason.

Summary

Coverage options are the verification-plan-to-coverage interface. at_least controls the per-bin closure threshold. weight controls the contribution to the covergroup headline. goal sets the closure target. comment carries the audit-trail citation. per_instance preserves per-instance visibility and cross-regression merging. auto_bin_max tunes the default bin partition. illegal_bins_error forces fatal severity on contract violations. name and set_inst_name() label instances for readability. The option-precedence hierarchy resolves conflicts most-specific-wins. Every non-default option carries semantic weight; treating them as cosmetic is the anti-pattern this page exists to prevent. Module 11.9 builds on this foundation with the sampling-event vocabulary — when, exactly, the coverage data lands in the bins.