SYSTEMVERILOG SERIES · SV-20B

SystemVerilog Series — SV-20b: Coverage Points, Cross, Options & Methods — VLSI Trainers
SystemVerilog Series · SV-20b

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
SyntaxNumber of bins createdBin naming
bins name = {range_list}1name
bins name[] = {range_list}One per value in rangename[value]
bins name[N] = {range_list}N (values distributed uniformly)name[0] to name[N-1]
bins name = defaultOne per uncovered valuename[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
}
Cannot ignore_bins a default bin. It is a compile error to write 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 typeNumber of auto bins (N)Bin naming
Enum typeOne per enumeration valueauto[EnumName]
Other integral (M bits)min(2ᴹ, auto_bin_max) — default auto_bin_max = 64auto[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
auto_bin_max default is 64. For a 32-bit integer, 2ᴹ = 4 billion — far too many. SV automatically caps at 64, grouping the 4 billion values into 64 equal-sized ranges. Use 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]);
OperatorMeaningMultiple bins []?
val [* N]Exact consecutive repetition N timesYes (if N is fixed)
val [* N:M]Consecutive repetition N to M timesYes (if bounded)
val [-> N]Exactly N non-consecutive occurrences; ends at last matchYes
val [-> N:M]N to M non-consecutive occurrences; ends at last matchYes
val [= N]N non-consecutive occurrences; can extend past last matchNo — 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)
illegal_bins are assertions on coverage data. Use them when a value in the coverage point should never occur during a correct simulation. If it does, it signals a design bug or a testbench misconfiguration. Unlike $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 on transition bins: when select expressions are used with transition bins, the 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
per_instance can only be set in the covergroup definition — not procedurally. All other instance options can be set either inline or procedurally after instantiation.

📋 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.

OptionCovergroupCoverpointCrossActs as default?
nameNo
weightNo — does not propagate down
goalNo — does not propagate down
commentNo — does not propagate down
at_least✓ defaultYes — propagates to points and crosses
detect_overlap✓ defaultYes — propagates to coverpoints only
auto_bin_max✓ defaultYes — propagates to coverpoints only
cross_auto_bin_max✓ defaultYes — propagates to crosses only
cross_num_print_missing✓ defaultYes — propagates to crosses only
per_instanceNo — covergroup level only
Type optionCovergroupCoverpointCrossActs as default?
weightNo — does not propagate down
goalNo
commentNo
strobeNo — 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.

void sample()
Covergroup only
Manually trigger one sampling of the covergroup. Required when no clocking event is defined.
real get_coverage()
Covergroup, coverpoint, cross
Returns the cumulative type-level coverage (0.0 to 100.0) for all instances of this covergroup type.
real get_inst_coverage()
Covergroup, coverpoint, cross
Returns the per-instance coverage (0.0 to 100.0) for this specific instance.
void set_inst_name(string)
Covergroup only
Assigns a custom name to this instance in coverage reports and databases.
void start()
Covergroup, coverpoint, cross
Resume coverage collection. Coverage collection begins automatically at construction — use after stop() to re-enable.
void stop()
Covergroup, coverpoint, cross
Pause coverage collection. Useful during reset or other periods when sampling is meaningless.
real query()
Covergroup, coverpoint, cross
Returns the cumulative (type-level) coverage number for this group type across all instances.
real inst_query()
Covergroup, coverpoint, cross
Returns the per-instance coverage number for this specific instance only.
// 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.

covergroup option
stringname
intweight
intgoal
stringcomment
intat_least
intauto_bin_max
intcross_auto_bin_max
intcross_num_print_missing
bitdetect_overlap
bitper_instance
coverpoint option
intweight
intgoal
stringcomment
intat_least
intauto_bin_max
bitdetect_overlap

cross option
intweight
intgoal
stringcomment
intat_least
intcross_auto_bin_max
intcross_num_print_missing
covergroup type_option
intweight
intgoal
stringcomment
bitstrobe

coverpoint/cross type_option
intweight
intgoal
stringcomment

📋 Quick Reference

Bin type summary

KeywordPurposeCounts toward coverage?Runtime error?
binsDefine a coverage binYesNo
bins name = defaultCatch all uncovered valuesNoNo
ignore_binsExclude values/transitions silentlyNoNo
illegal_binsExclude values/transitions as illegalNoYes
wildcard binsMatch 2-state pattern with ?/X/Z as wildcardsYesNo

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_bins take priority over all other bins including ignore_bins.
  • The default bin is excluded from coverage % — diagnostic only.
  • [] (array of bins) cannot be used with unbounded or variable-length transitions.
  • type_option values must be constants; option values can use arguments or expressions.
  • per_instance can only be set in the covergroup definition, not procedurally.
  • strobe=1 moves sampling to the Postponed region — one sample per time slot regardless of multiple triggers.
Section 20 complete. Coming next: SV-21 covers Parameters — typed parameters, type parameters (parameter type T), localparam in generate blocks, the $ unbounded token, and $isunbounded().

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top