Coverage
Functional coverage concepts, the covergroup construct as a user-defined type, coverage points, cross coverage, clocking and block events for sampling, formal arguments including ref, and embedding covergroups inside classes for tightly-bound property coverage.
📈 Functional Coverage
Functional coverage is a user-defined metric that measures how much of the design specification has been exercised by simulation. Unlike code coverage (which is inferred automatically from design code), functional coverage is written explicitly to reflect the intent of the design — what scenarios, corner cases, and invariants must be tested.
- User-specified — not automatically inferred from the design code or its structure.
- Specification-based — tied to the design intent, independent of implementation details.
- Used to measure whether interesting scenarios, corner cases, and required protocol sequences have been observed and validated.
Functional coverage is used as a guide: when coverage is high, the verification team can redirect simulation effort to areas not yet exercised rather than running redundant tests.
📋 Code vs Functional Coverage
Code Coverage
- Automatically extracted from design source
- Measures lines, branches, expressions, FSM states hit
- Independent of design intent
- Does not tell you if interesting scenarios were exercised
- 100% code coverage does not mean 100% of specification exercised
Functional Coverage
- User-written coverage model
- Directly tied to the verification plan
- Measures specific values, transitions, combinations
- Can cover corner cases, protocol sequences, error conditions
- Requires up-front effort but yields higher confidence
The SV functional coverage constructs enable: coverage of variables and expressions; automatic and user-defined coverage bins; bins for values, transitions, and cross products; filtering conditions; event- and sequence-triggered sampling; and procedural activation and query of coverage data.
📄 The covergroup Construct
A covergroup is a user-defined type that encapsulates an entire coverage model. The type is defined once, and any number of instances can be created via new(). It can be defined inside a module, program, interface, or class.
// Define the covergroup type covergroup cg; // ... coverage points, cross, options endgroup // Create an instance cg cg_inst = new; // Optional closing name — must match opening name covergroup my_cov; // ... endgroup : my_cov // Multiple instances of the same covergroup type cg inst_a = new; cg inst_b = new; // independent coverage tracking per instance
new(). Multiple instances track coverage independently — useful when the same coverage model applies to multiple bus channels, ports, or objects.
📋 Five Components of a Covergroup
sample() manually.ref variables.// Showing all five components covergroup g2 @(posedge clk); // ① clocking event Hue: coverpoint pixel_hue; // ② coverage point (labeled) Offset: coverpoint pixel_offset; // ② coverage point (labeled) AxC: cross color, pixel_adr; // ③ cross (2 implicit coverpoints) all: cross color, Hue, Offset; // ③ cross (1 var + 2 explicit points) option.per_instance = 1; // ⑤ coverage option endgroup
📋 Type and Instance — new()
The covergroup type is defined once. Instances are created with new(). Instance options like name and comment can differ per instance; type options like weight and goal apply to all instances.
// Simple type and instance covergroup cg; ... endgroup cg cg_inst = new; // Instance options can be set after creation cg_inst.option.comment = "Bus A coverage"; cg_inst.a.option.weight = 3; // weight for coverpoint 'a' of this instance // Type options set with :: — affect all instances gc::type_option.comment = "Global coverage model"; gc::a::type_option.weight = 3;
⏱ Clocking Events
A clocking event specifies when coverage points are sampled automatically. Without one, the covergroup must be sampled procedurally by calling .sample().
// Sample on every posedge clk covergroup cg1 @(posedge clk); coverpoint state; endgroup // Sample on any change of member m_z (event-based) covergroup cg2 @m_z; coverpoint data; endgroup // No clocking event — must call sample() manually covergroup cg3; coverpoint addr; endgroup cg3 c = new; initial begin @(posedge clk); c.sample(); // trigger sampling explicitly end
type_option.strobe = 1 to restrict sampling to the Postponed region — once per time slot regardless of multiple triggers.
📋 Block Event Expressions
Instead of a clocking signal, a covergroup can trigger on the start or end of execution of a named block, task, function, or class method.
// Trigger on BEGIN of task "write_burst" covergroup cg_begin @@(begin write_burst); coverpoint burst_len; endgroup // Trigger on END of task "write_burst" covergroup cg_end @@(end write_burst); coverpoint status; endgroup // OR: fire on end of either of two functions covergroup cg_or @@(end read_bus or end write_bus); coverpoint return_code; endgroup
@@(begin task_name)— fires immediately before the task’s first statement.@@(end task_name)— fires immediately after the task’s last statement.- The end-of-execution trigger does not fire if the block is
disabled. - Multiple conditions can be OR-ed:
@@(end f1 or end f2).
📋 Coverage Points
A coverage point samples an integral variable or expression at each clocking event. It can have an optional label (becoming the point’s name) and an optional iff guard condition.
// Labeled coverage point on an enum — 3 auto bins (red, green, blue) enum { red, green, blue } color; covergroup g1 @(posedge clk); c: coverpoint color; // label 'c' creates the coverpoint name endgroup // Guard condition: sample s0 only when reset is inactive covergroup g4; coverpoint s0 iff(!reset); // coverage ignored when reset is true endgroup // Explicit bins: named ranges covering a 10-bit variable bit [9:0] v_a; covergroup cg @(posedge clk); coverpoint v_a { bins a = { [0:63], 65 }; // one bin for this value set bins b[] = { [127:150],[148:191] }; // one bin per value (65 bins) bins c[] = { 200, 201, 202 }; // three individual bins bins d = { [1000:$] }; // $ = maximum value of v_a bins others[] = default; // catch-all: one bin per uncovered value } endgroup
Bin forms
| Syntax | Effect |
|---|---|
| bins name = {range_list} | One bin for the entire value set |
| bins name[] = {range_list} | One bin per value in the set (array of bins) |
| bins name[N] = {range_list} | N fixed bins; values distributed uniformly |
| bins name = default | One bin per value not covered by other bins (not counted in coverage %) |
| ignore_bins name = {values} | Exclude these values from coverage entirely |
| illegal_bins name = {values} | Exclude and report runtime error if seen |
| wildcard bins name = {4’b11??} | X/Z/? treated as 0 or 1 wildcard (2-state match) |
Automatic bin creation
- If no bins are defined, SV automatically creates bins.
- For enums: one bin per enumeration value.
- For other integral types: N = min(2ᴹ,
auto_bin_max) bins, where M = bit-width. Defaultauto_bin_max= 64. - If N < 2ᴹ, values are distributed uniformly across N bins; the last bin absorbs any remainder.
- Automatic bins are named
auto[value]orauto[low:high]. - X and Z values are excluded from automatic bins.
✕ Cross Coverage
Cross coverage tracks the Cartesian product of two or more coverage points. Every combination of bins from the participating points becomes a cross-product bin. When a variable (not a coverpoint) is listed in a cross, SV implicitly creates a coverpoint for it.
// Simple 2-variable cross: 16×16 = 256 cross-product bins bit [3:0] a, b; covergroup cov @(posedge clk); aXb : cross a, b; // implicit coverpoints for a and b endgroup // Expression coverpoint crossed with a variable covergroup cov2 @(posedge clk); BC: coverpoint b+c; // explicit coverpoint for expression aXb: cross a, BC; // a implicitly created; BC already explicit endgroup // Explicit cross bins using select expressions covergroup cg @(posedge clk); a: coverpoint v_a { bins a1={[0:63]}; bins a2={[64:127]}; } b: coverpoint v_b { bins b1={0}; bins b2={[1:84]}; } c: cross v_a, v_b { // cross products where a does NOT intersect [100:200] → 4 products bins c1 = !binsof(a) intersect {[100:200]}; // cross products in a2 OR in b2 → 7 products bins c2 = binsof(a.a2) || binsof(b.b2); // cross products in a1 AND in b4 → 1 product bins c3 = binsof(a.a1) && binsof(b.b2); // ignore: exclude specific cross products from coverage ignore_bins foo = binsof(a) intersect {5, [1:3]}; } endgroup
binsof(cp) yields all bins of a coverpoint. binsof(cp.bin_name) selects a specific named bin. binsof(cp) intersect {range} selects only bins whose values overlap the range. The negated form !binsof(cp) intersect {range} selects bins whose values do NOT overlap. Combine with && and || for complex cross bin selection.
📋 Coverage Options
Options control binning behaviour, goals, weights, and reporting. Instance options (option.name) are per-instance; type options (type_option.name) apply to all instances of the covergroup type.
| Instance option | Default | What it controls |
|---|---|---|
| weight | 1 | Weight of this point/cross toward overall coverage |
| goal | 90 | Target coverage percentage for this instance/point |
| name | unique | Explicit name for this instance in coverage reports |
| comment | “” | Comment stored in coverage database and reports |
| at_least | 1 | Minimum hits per bin to count as covered |
| auto_bin_max | 64 | Max automatic bins for coverpoints without explicit bins |
| cross_auto_bin_max | unbounded | Max automatic cross product bins |
| detect_overlap | 0 | Warn if bin ranges overlap within a coverpoint |
| per_instance | 0 | Track coverage individually per instance (in addition to type-level) |
| Type option | Default | What it controls |
|---|---|---|
| weight | 1 | Weight toward the simulation-wide cumulative coverage |
| goal | 90 | Target for the covergroup type overall |
| comment | “” | Comment for the covergroup type in reports |
| strobe | 0 | 1 = sample in Postponed region (one sample per time slot) |
// Options inline in the covergroup definition covergroup g1(int w, string instComment) @(posedge clk); option.per_instance = 1; option.comment = instComment; type_option.strobe = 1; // sample once per time slot a: coverpoint a_var { option.auto_bin_max = 128; // 128 bins for this point } b: coverpoint b_var { option.weight = w; // instance option set from argument type_option.weight = 5; // type option — must be a constant } endgroup
📋 Formal Arguments and ref
Covergroups can accept arguments, allowing generic coverage models reusable across different variables and ranges. A ref argument causes the covergroup to track the variable — sampling its current value at each clock tick. An input argument captures the value once at new() time.
// Generic covergroup with ref variable and range arguments covergroup gc(ref int ra, int low, int high) @(posedge clk); coverpoint ra { // ra sampled by reference at each clock bins good = { [low : high] }; // range from input args bins bad[] = default; } endgroup int va, vb; gc c1 = new(va, 0, 50); // c1 tracks va; good = [0:50] gc c2 = new(vb, 120, 600); // c2 tracks vb; good = [120:600] // ref vs input distinction: // ref int ra → ra is sampled at every clocking event (tracks variable) // int low → low is evaluated once when new() is called (snapshot)
📋 Covergroups in Classes
Embedding a covergroup inside a class tightly binds coverage to the class properties — including local and protected members that would otherwise be inaccessible from outside. The embedded covergroup becomes part of the class’s type definition.
// Simple embedded covergroup class xyz; bit [3:0] m_x; int m_y; bit m_z; covergroup cov1 @m_z; // samples on change of m_z coverpoint m_x; coverpoint m_y; endgroup function new(); cov1 = new; endfunction // MUST instantiate in new() endclass // Multiple covergroups in one class — different sampling events class MC; logic [3:0] m_x; local logic m_z; // local — only embedded covergroup can access bit m_e; covergroup cv1 @(posedge clk); coverpoint m_x; endgroup covergroup cv2 @m_e; coverpoint m_z; endgroup // cv2 covers local m_z — impossible from outside the class endclass
📋 Embedded Covergroup Rules
- When a covergroup is defined in a class without an explicit variable declaration, a variable with the same name as the covergroup is implicitly declared.
- Each class contains exactly one variable of each embedded covergroup type. Declaring multiple variables of the same embedded covergroup is a compile error.
- The embedded covergroup must be explicitly instantiated in the class’s
new()method by assigningcg_name = new(...). If it is not, the covergroup is never created and no data is sampled. - Embedded covergroups can access any class member — including
localandprotected— without changing the class’s data encapsulation.
📋 Advanced Embedded Patterns
Covergroup referencing a member of a nested object
class Helper; int m_ev; endclass class MyClass; Helper m_obj; int m_a; covergroup Cov @(m_obj.m_ev); // trigger on m_obj.m_ev change coverpoint m_a; endgroup function new(); m_obj = new; // MUST create m_obj before Cov! Cov = new; // Cov references m_obj.m_ev — m_obj must exist first endfunction endclass
Covergroup argument used to set an option
class C1; bit [7:0] x; covergroup cv(int arg) @(posedge clk); option.at_least = arg; // each bin must be hit 'arg' times to count coverpoint x; endgroup function new(int p1); cv = new(p1); // pass in minimum hit count at construction time endfunction endclass initial begin C1 obj = new(4); // each bin must be hit at least 4 times end
📋 Quick Reference
Covergroup structure
| Item | Syntax | Notes |
|---|---|---|
| Define type | covergroup name; … endgroup | Type only — no sampling yet |
| Instantiate | name inst = new(args) | Sampling begins at construction |
| Clock trigger | covergroup cg @(posedge clk) | Auto-samples at every trigger |
| Block trigger | covergroup cg @@(end task_name) | Samples at start/end of named block |
| Manual sample | inst.sample() | Required when no clocking event |
| Coverage point | label: coverpoint expr; | Label becomes coverpoint name |
| Guard condition | coverpoint x iff(!reset) | Skip sample when condition false |
| Cross | label: cross cp1, cp2; | Cartesian product of all bins |
Key rules
- Covergroup is a type — instance must be created with
new(). - Without a clocking event, call
.sample()procedurally. - The
defaultbin catches uncovered values but does not count toward coverage %. - Automatic bins: enum → one per value; others → min(2ᴹ,
auto_bin_max) bins. - X/Z values excluded from automatic bins and wildcard bins.
- Embedded covergroups access all class members including
localandprotected. - Embedded covergroups must be instantiated in
new()— or they never collect data. - Object dependencies (e.g.
m_obj.m_ev) must be created before the covergroup is instantiated. type_optionvalues must be constants;optionvalues can be expressions evaluated atnew()time.per_instancecan only be set in the covergroup definition, not procedurally afterward.
parameter type), localparam in generate blocks, the $ token for unbounded ranges, $isunbounded(), and parameter dependencies.
