Coverage Points, Cross Coverage, Options & Methods
Full detail on coverage point bins — value, transition, automatic, wildcard, ignore, illegal; cross coverage and binsof select expressions; instance and type options with placement rules; predefined coverage methods and system tasks; and the internal structure of the option members.
📋 Coverage Points
A coverage point samples an integral variable or expression at each clocking event. Its name is the label (if given), or the variable name (if the expression is a single variable), or a tool-generated name (for complex expressions — which cannot be referenced in the language).
// Labeled coverpoint — name 'state_cp' usable in cross and methods state_cp: coverpoint state; // Unlabeled on a variable — name is 'state' coverpoint state; // Expression coverpoint — must be labeled to be usable in cross ARITH: coverpoint a + b; // Guard condition: only sample when the enable condition is true coverpoint s0 iff(!reset); // ignored whenever reset is high // Clocking-block signal: samples from the Preponed or Observed region // depending on the clocking block's input skew setting // #1step → Preponed region (settled value from previous time step) // #0 → Observed region (post-NBA value in current time step) covergroup cg @(cb); coverpoint cb.data; // samples the clocking-block skewed value endgroup
📋 Bin Forms — Value Bins
Bins associate a name and a hit counter with a set of values. The counter is incremented each time the sampled expression matches any value in the bin’s set.
bit [9:0] v_a; covergroup cg @(posedge clk); coverpoint v_a { // ONE bin for the entire range [0:63] plus value 65 bins a = { [0:63], 65 }; // ONE BIN PER VALUE in [127:191] (overlapping ranges merged) // Creates: b[127], b[128], ... b[191] — 65 bins total // Note: [127:150] and [148:191] overlap at 148..150 — values still merged bins b[] = { [127:150], [148:191] }; // One bin per value in the list: c[200], c[201], c[202] bins c[] = { 200, 201, 202 }; // ONE bin for all values from 1000 to max ($ = 1023 for 10-bit v_a) bins d = { [1000:$] }; // CATCH-ALL: one bin per uncovered value (not counted in coverage %) bins others[] = default; } endgroup
| Syntax | Number of bins created | Bin naming |
|---|---|---|
| bins name = {range_list} | 1 | name |
| bins name[] = {range_list} | One per value in range | name[value] |
| bins name[N] = {range_list} | N (values distributed uniformly) | name[0] to name[N-1] |
| bins name = default | One per uncovered value | name[value] |
📋 Fixed-Count Bins
When you specify bins name[N], the values in the range are distributed as uniformly as possible across N bins. If the count doesn’t divide evenly, the last bin absorbs the remainder.
// bins fixed[3] = {1:10} — 11 values distributed across 3 bins // Bin 0: values {1,2,3} (3 values) // Bin 1: values {4,5,6} (3 values) // Bin 2: values {7,8,9,10} (4 values — last bin takes the remainder) bins fixed[3] = {1:10}; // If N > number of values → some bins are empty // bins sparse[10] = {0,1} → bins sparse[0]={0}, sparse[1]={1}, // sparse[2..9] are empty // Practical use: group a 32-bit address space into regions bit [31:0] addr; covergroup addr_cov @(posedge clk); coverpoint addr { bins region[8] = {[32'h0:32'hFFFFFFFF]}; // 8 equal address regions } endgroup
📋 The default Bin
The default bin catches all values of the coverage point that do not fall in any explicitly defined bin. It is excluded from the coverage percentage calculation — a hit to the default bin never contributes to coverage. It is purely diagnostic: for spotting values you didn’t anticipate.
covergroup fsm_cov @(posedge clk); coverpoint state { bins idle = { IDLE }; bins req = { REQ }; bins grant = { GRANT }; bins other[] = default; // catches unexpected states — does NOT count toward coverage } endgroup // default sequence form: catches all transition sequences not in any defined transition bin // Only allowed when transition bins are defined — cannot use [] with default sequence coverpoint v_a { bins sa = (4=>5=>6); bins allother = default sequence; // catches all other transition sequences }
ignore_bins x = default. The default bin exists precisely to catch everything else — ignoring it makes no semantic sense. Similarly, you cannot use [] with default sequence — only a single bin is allowed.
📋 Per-Bin Guard Condition
A guard condition can be attached to individual bins using iff(expr). If the condition is false at the sampling point, that bin’s counter is not incremented even though the coverpoint value matches.
covergroup mode_cov @(posedge clk); coverpoint opcode { bins read = {2'b00} iff(!busy); // count read only when bus is idle bins write = {2'b01} iff(!busy); bins nop = {2'b11}; // no guard — always counted } endgroup // Point-level vs bin-level guard: // iff on coverpoint → all bins in this point are gated // iff on individual bin → only that bin is gated
📋 Automatic Bin Creation
When a coverpoint has no explicit bins, SV automatically creates bins. The number and naming follow predictable rules.
| Coverage point type | Number of auto bins (N) | Bin naming |
|---|---|---|
| Enum type | One per enumeration value | auto[EnumName] |
| Other integral (M bits) | min(2ᴹ, auto_bin_max) — default auto_bin_max = 64 | auto[value] or auto[low:high] |
// Example: 3-bit variable → M=3, 2^M=8 possible values // With auto_bin_max=64: N=8 → one bin per value: auto[0]..auto[7] // With auto_bin_max=3: N=3 → 8 values spread across 3 bins: // auto[0:1], auto[2:3], auto[4:7] (last bin absorbs remainder) enum {IDLE, REQ, GRANT, DONE} state; covergroup fsm @(posedge clk); coverpoint state; // auto creates: auto[IDLE], auto[REQ], auto[GRANT], auto[DONE] endgroup // X and Z are ALWAYS excluded from automatic bins
option.auto_bin_max = N to control this explicitly.
🔂 Transition Bins
Transition bins count sequences of sampled values across consecutive clock ticks. The syntax uses => to separate values at consecutive sample points.
bit [4:1] v_a; covergroup cg @(posedge clk); coverpoint v_a { // Single bin for ALL of these sequences: // 4=>5=>6 or 7=>11 or 8=>11 or ... 10=>12 bins sa = (4=>5=>6), ([7:9],10=>11,12); // One bin PER sequence: // sb[4=>5=>6], sb[7=>11], sb[7=>12], sb[8=>11], ..., sb[10=>12] bins sb[] = (4=>5=>6), ([7:9],10=>11,12); // Catch all other transition sequences not matching sa or sb bins allother = default sequence; } endgroup // Set expansion: range_list1 => range_list2 expands to all combinations // 1,5 => 6,7 → 1=>6, 1=>7, 5=>6, 5=>7 (four transitions) bins cross_trans[] = (1,5 => 6,7);
🔂 Transition Repetition Forms
[* N] — Consecutive repetition
// 3[*5] = 3=>3=>3=>3=>3 bins five_threes = (3[*5]); // Range: 3[*3:5] matches any of: // 3=>3=>3 // 3=>3=>3=>3 // 3=>3=>3=>3=>3 bins rep_range[] = (3[*3:5]); // Cannot use [] (multiple-bin) with // unbounded repetition (e.g. [*3:$])
[-> N] — Goto (non-consecutive)
// 3[->3] = ...3=>...=>3...=>3 // Value 3 appears exactly 3 times; // any non-3 values in between are OK bins goto3 = (3[->3]); // 3[=2] = non-consecutive, but match // can extend PAST the last occurrence // (as long as 3 stays false after) bins nonconsec = (3[=2]);
| Operator | Meaning | Multiple bins []? |
|---|---|---|
| val [* N] | Exact consecutive repetition N times | Yes (if N is fixed) |
| val [* N:M] | Consecutive repetition N to M times | Yes (if bounded) |
| val [-> N] | Exactly N non-consecutive occurrences; ends at last match | Yes |
| val [-> N:M] | N to M non-consecutive occurrences; ends at last match | Yes |
| val [= N] | N non-consecutive occurrences; can extend past last match | No — unbounded length |
📋 Wildcard Bins
The wildcard keyword treats X, Z, and ? in a bin definition as placeholders for both 0 and 1, matching any 2-state value that fits the pattern. Sampled values containing actual X or Z are excluded.
// wildcard bins g12_16 = { 4'b11?? } // ? expands to 0 and 1 independently at each bit position // Matches: 1100, 1101, 1110, 1111 → decimal 12, 13, 14, 15 wildcard bins g12_16 = { 4'b11?? }; // Wildcard transition bin // wildcard bins T0_3 = (2'b0x => 2'b1x) // x expands to 0 and 1 independently: // Matches: 00=>10, 00=>11, 01=>10, 01=>11 wildcard bins T0_3 = (2'b0x => 2'b1x); // Practical pattern: any valid read address (top 4 bits = region code) // wildcard bins region3 = { 8'b0011???? } → matches 0x30..0x3F wildcard bins region3 = { 8'b0011???? }; // Rules: // Low bound computed by replacing each ? with 0 // High bound computed by replacing each ? with 1 // Actual X or Z values in the signal are EXCLUDED from wildcard bins
⛔ ignore_bins
ignore_bins permanently excludes a set of values or transitions from coverage. An ignored value is never counted — even if it also appears in another bins definition.
covergroup cg23; coverpoint a { ignore_bins ignore_vals = {7, 8}; // values 7 and 8 never counted ignore_bins ignore_trans = (1=>3=>5); // this transition sequence never counted } endgroup // Ignored values are excluded even from bins that include them: // bins low = {[0:10]} would normally count a=7 and a=8 — ignore_bins prevents it // Practical use: exclude values that cannot occur (e.g. reserved opcodes) // or exclude reset sequences that would pollute coverage data covergroup opcode_cov; coverpoint opcode { bins all_ops[] = {[0:15]}; ignore_bins reserved = {9, 10, 11}; // opcodes 9-11 are reserved } endgroup
📋 illegal_bins
illegal_bins marks a set of values or transitions as protocol violations. They are excluded from coverage, and if they occur, a run-time error is reported. Illegal bins take priority over all other bins — a runtime error fires even if the same value also appears in a regular bins or ignore_bins.
covergroup cg3; coverpoint b { illegal_bins bad_vals = {1, 2, 3}; // runtime error if seen illegal_bins bad_trans = (4=>5=>6); // runtime error if this sequence occurs } endgroup // ignore_bins vs illegal_bins: // ignore_bins — silently excluded; no error, just not counted // illegal_bins — excluded AND runtime error issued; design protocol violation // Priority: illegal_bins > ignore_bins > bins // If a value appears in both illegal_bins and bins: // → runtime error fires (illegal takes precedence)
$error in assertions, the runtime error from illegal_bins is simulation-tool-dependent in its severity.
✕ Cross Coverage
Cross coverage measures the Cartesian product of two or more coverage points — checking that every combination of bins has been exercised. When a variable is listed in a cross, SV implicitly creates a coverpoint for it. Expressions cannot appear directly in a cross — they must first be declared as a labeled coverpoint.
// Simple cross — 4-bit a and b → 16 bins each → 256 cross products bit [3:0] a, b; covergroup cov @(posedge clk); aXb: cross a, b; // implicit coverpoints for a and b // 16 auto bins for a, 16 for b → 256 cross products endgroup // Cross involving an expression coverpoint covergroup cov2 @(posedge clk); BC: coverpoint b+c; // MUST be labeled to use in cross aXb: cross a, BC; endgroup // Cross with an explicit-bins coverpoint // a_var has 32 bits — auto bins NOT used; only yy[0]..yy[9] (10 bins) // b_var has 4 bits — 16 auto bins // → CC has 16 × 10 = 160 cross product bins bit [31:0] a_var; bit [3:0] b_var; covergroup cov3 @(posedge clk); A: coverpoint a_var { bins yy[] = { [0:9] }; } CC: cross b_var, A; // b_var implicit: 16 bins; A explicit: 10 bins → 160 products endgroup // Cross guard: skip cross entirely when reset is active aXb: cross a, b iff(!reset); // Cross is ONLY valid between coverpoints in the SAME covergroup
📋 binsof and select Expressions
User-defined cross bins use binsof select expressions to name specific groups of cross products. This lets you group, filter, and rename the Cartesian product into meaningful coverage goals.
// binsof(cp) → all bins of coverpoint cp // binsof(cp.binname) → just the named bin from cp // binsof(cp) intersect {range} → bins of cp whose values overlap range // !binsof(cp) intersect {range} → bins of cp whose values DON'T overlap range // Open range syntax in intersect: // [$ : value] → all values ≤ value // [value : $] → all values ≥ value // Select operators: && (both conditions) || (either condition) ! (negate) bins low_a_any_b = binsof(a) intersect {[$:63]}; // a ≤ 63, any b bins hi_a_any_b = binsof(a) intersect {[128:$]}; // a ≥ 128, any b bins only_a2 = binsof(a.a2); // only products where a is in bin a2 bins a2_or_b2 = binsof(a.a2) || binsof(b.b2); // either a in a2, or b in b2 bins a1_and_b4 = binsof(a.a1) && binsof(b.b4); // a in a1 AND b in b4 bins not_mid_a = !binsof(a) intersect {[64:191]}; // a outside [64:191]
binsof operator uses the last value of the transition to determine which bins to select. For example, binsof(a) intersect {5} would select any transition bin where the ending value of a is 5.
📋 Full Cross Example — 4×4 with Named Bins
bit [7:0] v_a, v_b; covergroup cg @(posedge clk); // Coverpoint a: 4 equal-sized bins a: coverpoint v_a { bins a1 = { [0:63] }; // low quarter bins a2 = { [64:127] }; // second quarter bins a3 = { [128:191] }; // third quarter bins a4 = { [192:255] }; // high quarter } // Coverpoint b: 4 bins with different sizing b: coverpoint v_b { bins b1 = { 0 }; // zero only bins b2 = { [1:84] }; // low range bins b3 = { [85:169] }; // mid range bins b4 = { [170:255] }; // high range } // Cross: without user bins → 16 products (<a1,b1>..<a4,b4>) // With user bins → explicitly named subsets replace the full 16 c: cross v_a, v_b { // c1: a bins that DON'T intersect [100:200] → only a1 qualifies → 4 products // <a1,b1>, <a1,b2>, <a1,b3>, <a1,b4> bins c1 = !binsof(a) intersect {[100:200]}; // c2: products in a2 OR in b2 → 7 products // <a2,b1>, <a2,b2>, <a2,b3>, <a2,b4>, // <a1,b2>, <a3,b2>, <a4,b2> bins c2 = binsof(a.a2) || binsof(b.b2); // c3: products in a1 AND in b4 → 1 product: <a1,b4> bins c3 = binsof(a.a1) && binsof(b.b4); } endgroup
⛔ Cross ignore_bins and illegal_bins
// ignore_bins in a cross: exclude specific cross products from coverage covergroup yy; cross a, b { ignore_bins foo = binsof(a) intersect { 5, [1:3] }; // All cross products where a=1,2,3, or 5 are excluded from coverage // Ignored cross products are excluded even if they match another cross bin } endgroup // illegal_bins in a cross: exclude AND report runtime error covergroup zz(int bad); cross x, y { illegal_bins foo = binsof(y) intersect {bad}; // Runtime error if any cross product has y = bad // illegal_bins take precedence over all other cross bins // A runtime error fires even if the same product is in ignore_bins } endgroup
📋 Instance Options — option.*
Instance options affect only the specific covergroup instance they are set on. They are set using option.name = value — either inline in the covergroup definition (evaluated at new() time) or procedurally after creation.
// Setting options inline in the definition (evaluated at new() time) covergroup g1(int w, string instComment) @(posedge clk); option.per_instance = 1; // track each instance separately option.comment = instComment; // per-instance comment from argument a: coverpoint a_var { option.auto_bin_max = 128; // this point gets 128 auto bins instead of 64 } b: coverpoint b_var { option.weight = w; // argument used as weight } c1: cross a_var, b_var; endgroup // Creating two instances with different options g1 tx_cov = new(2, "TX channel coverage"); g1 rx_cov = new(3, "RX channel coverage"); // Setting options procedurally after instantiation covergroup gc @(posedge clk); a: coverpoint a_var; b: coverpoint b_var; endgroup gc g1 = new; g1.option.comment = "Instance g1 comment"; // set on the instance g1.a.option.weight = 3; // set on coverpoint 'a' of this instance
📋 Type Options — type_option.*
Type options apply to all instances of the covergroup type simultaneously — like static class members. They must use constant expressions (not variables or arguments). Set inline with type_option.name = const or procedurally via CovgroupType::type_option.name = expr.
// Inline type options — constants only covergroup g1(int w, string instComment) @(posedge clk); type_option.comment = "Coverage model for bus features"; type_option.strobe = 1; // sample once per time slot (Postponed region) a: coverpoint a_var { option.weight = 2; // instance weight for point 'a' type_option.weight = 3; // type weight for point 'a' (must be constant) // type_option.weight = w → SYNTAX ERROR: w is not a constant } b: coverpoint b_var { option.weight = w; // instance weight from argument — OK type_option.weight = 5; // type weight — constant only } endgroup // Procedural type option assignment — any time during simulation gc::type_option.comment = "Updated comment"; gc::a::type_option.weight = 3; // set type weight for coverpoint 'a' of gc
📋 Option Placement Rules
Not all options are valid at all syntactic levels. When set at the covergroup level, most options act as defaults for all contained coverpoints and crosses — which can override them individually. Four options (weight, goal, comment, per_instance) do not cascade downward when set at the covergroup level.
| Option | Covergroup | Coverpoint | Cross | Acts as default? |
|---|---|---|---|---|
| name | ✓ | — | — | No |
| weight | ✓ | ✓ | ✓ | No — does not propagate down |
| goal | ✓ | ✓ | ✓ | No — does not propagate down |
| comment | ✓ | ✓ | ✓ | No — does not propagate down |
| at_least | ✓ default | ✓ | ✓ | Yes — propagates to points and crosses |
| detect_overlap | ✓ default | ✓ | — | Yes — propagates to coverpoints only |
| auto_bin_max | ✓ default | ✓ | — | Yes — propagates to coverpoints only |
| cross_auto_bin_max | ✓ default | — | ✓ | Yes — propagates to crosses only |
| cross_num_print_missing | ✓ default | — | ✓ | Yes — propagates to crosses only |
| per_instance | ✓ | — | — | No — covergroup level only |
| Type option | Covergroup | Coverpoint | Cross | Acts as default? |
|---|---|---|---|---|
| weight | ✓ | ✓ | ✓ | No — does not propagate down |
| goal | ✓ | ✓ | ✓ | No |
| comment | ✓ | ✓ | ✓ | No |
| strobe | ✓ | — | — | No — covergroup level only |
📋 Coverage Calculation with Weights
The instance coverage of a covergroup is the weighted average of all its coverpoints and crosses. The type coverage is a separate weighted average computed using type_option.weight values.
// Given: g1 has coverpoints a (weight=2) and b (weight=w) // Instance coverage formula: // = (coverage(a) × 2 + coverage(b) × w) / (2 + w) // Type coverage formula (using type_option.weight: a→3, b→5): // = (type_coverage(a) × 3 + type_coverage(b) × 5) / (3 + 5) // Overall simulation coverage (from $get_coverage): // Weighted average of all covergroup type coverages in the simulation, // weighted by their type_option.weight values
▶ Predefined Coverage Methods
All coverage methods are functions that can be called procedurally at any point during simulation.
// Typical usage patterns my_cov c = new; // Manual sampling c.sample(); // Check coverage at end of test if(c.get_inst_coverage() < 90.0) $display("WARNING: coverage only %.1f%%", c.get_inst_coverage()); // Pause during reset @(negedge rst_n) c.stop(); @(posedge rst_n) c.start(); // Set instance name after construction c.set_inst_name("bus_A_cov");
📋 Coverage System Tasks and Functions
Three system-level tasks and functions manage the coverage database across an entire simulation run.
// Set the filename for the coverage database written at end of simulation $set_coverage_db_name("coverage.db"); // Load previously accumulated coverage data from a file // Merges with current simulation's coverage data // Useful for multi-run cumulative coverage analysis $load_coverage_db("previous_run.db"); // Return the overall coverage percentage (0.0 to 100.0) // across ALL covergroup types in the simulation real total = $get_coverage(); initial begin // Run simulation... $display("Total coverage: %.2f%%", $get_coverage()); if($get_coverage() < 95.0) $display("Coverage goal not met — add more tests"); end
📋 option and type_option Member Structures
The option and type_option members are implicitly declared structs. Their fields vary depending on whether they belong to a covergroup, a coverpoint, or a cross item.
📋 Quick Reference
Bin type summary
| Keyword | Purpose | Counts toward coverage? | Runtime error? |
|---|---|---|---|
| bins | Define a coverage bin | Yes | No |
| bins name = default | Catch all uncovered values | No | No |
| ignore_bins | Exclude values/transitions silently | No | No |
| illegal_bins | Exclude values/transitions as illegal | No | Yes |
| wildcard bins | Match 2-state pattern with ?/X/Z as wildcards | Yes | No |
Predefined methods at a glance
sample()— manual trigger (covergroup only).get_coverage()/get_inst_coverage()— type vs per-instance percentage.set_inst_name(s)— rename this instance in reports.start()/stop()— enable/disable collection.query()/inst_query()— return coverage numbers.
Key rules
- Expressions in a cross must be labeled coverpoints — cannot cross raw expressions.
- Variables in a cross get an implicit coverpoint automatically.
illegal_binstake priority over all other bins includingignore_bins.- The
defaultbin is excluded from coverage % — diagnostic only. [](array of bins) cannot be used with unbounded or variable-length transitions.type_optionvalues must be constants;optionvalues can use arguments or expressions.per_instancecan only be set in the covergroup definition, not procedurally.strobe=1moves sampling to the Postponed region — one sample per time slot regardless of multiple triggers.
parameter type T), localparam in generate blocks, the $ unbounded token, and $isunbounded().
