A coverpoint target is any integral expression, evaluated at the sampling instant. Cover the derived property the plan names instead of the raw signal.
The next listing is not run in this course's checks: the open simulators do not count covergroups. The bench at the end of the lesson counts the same bins with the course's bins model.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
class addr_coverage;
covergroup cg with function sample(bit [31:0] addr,
bit [7:0] len,
bit [31:0] a, b);
// Raw value? No: cover the property the plan cares about.
cp_align : coverpoint addr[1:0] {
bins word_aligned = {0};
bins half_aligned = {2};
bins unaligned[] = {1, 3};
}
// Relationship between two fields
cp_cross_4k : coverpoint ((addr % 4096) + len > 4096) {
bins crosses_boundary = {1};
bins stays_inside = {0};
}
// Arithmetic relationship
cp_cmp : coverpoint (a > b) {
bins a_gt_b = {1};
bins a_le_b = {0};
}
endgroup
function new();
cg = new();
endfunction
endclassThe expression sees the sample() arguments exactly at the sampling instant. Keep expressions side-effect-free: a stateful function call makes sampling order load-bearing.
Enum coverpoints are a free gift: one automatic named bin per literal. State machines and response codes get readable coverage from a single line, and a new literal adds a visible hole on its own.
The next listing is not run in this course's checks: the open simulators do not count covergroups. The bench at the end of the lesson counts the same bins with the course's bins model.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
typedef enum logic [2:0] {
IDLE, FETCH, DECODE, EXEC, MEM, WB
} state_e;
module fsm_cov (input logic clk, input logic rst_n, input state_e state);
// One bin per literal: IDLE, FETCH, DECODE, EXEC, MEM, WB
covergroup cg_fsm @(posedge clk);
cp_state : coverpoint state iff (rst_n);
// Override only where the plan needs more nuance:
cp_state_grouped : coverpoint state iff (rst_n) {
bins front[] = {IDLE, FETCH, DECODE};
bins back[] = {EXEC, MEM, WB};
}
endgroup
cg_fsm cg_inst = new();
endmoduleA 1-bit condition coverpoint already proves both outcomes occurred. Named bins make the report say so plainly.
The next listing is not run in this course's checks: the open simulators do not count covergroups. The bench at the end of the lesson counts the same bins with the course's bins model.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
typedef enum logic [1:0] { OKAY, EXOKAY, SLVERR, DECERR } resp_e;
class flag_coverage;
localparam int DEPTH = 8;
covergroup cg with function sample(bit [3:0] count, resp_e resp, bit wrapped);
cp_full : coverpoint (count == DEPTH) {
bins hit_full = {1};
bins not_full = {0};
}
cp_err : coverpoint (resp inside {SLVERR, DECERR}) {
bins errored = {1};
bins clean = {0};
}
cp_wrap : coverpoint wrapped {
bins wrapped = {1};
bins linear = {0};
}
endgroup
function new();
cg = new();
endfunction
endclassA coverpoint without explicit bins gets automatic bins from its type. The build caps auto-bin creation at auto_bin_max, which defaults to 64.
Expression width | Possible values | Auto bins created |
|---|---|---|
1 bit | 2 | 2 (auto[0], auto[1]) |
3 bits | 8 | 8, one per value |
6 bits | 64 | 64, one per value |
8 bits | 256 | 64 bucketed: 4 values per bin |
16 bits | 65536 | 64 bucketed: 1024 values per bin |
Enum, 6 literals | 6 | 6 named bins (special rule) |
Past 6 bits the tool silently squeezes values into 64 range buckets. An 8-bit field gets 4 values per bin, and the report looks plausible while measuring almost nothing specific.
The next listing is not run in this course's checks: the open simulators do not count covergroups. The bench at the end of the lesson counts the same bins with the course's bins model.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
class len_auto_coverage;
covergroup cg with function sample(bit [7:0] len);
// Silent bucketing: 256 values squeezed into 64 range bins
cp_len_auto : coverpoint len;
// Explicit control: raise the cap (rarely the right fix)...
cp_len_256 : coverpoint len { option.auto_bin_max = 256; }
// ...or, better, write intent-driven bins
cp_len_plan : coverpoint len {
bins zero = {0};
bins one = {1};
bins mid = {[2:254]};
bins max_len = {255};
}
endgroup
function new();
cg = new();
endfunction
endclassRaising the cap is rarely the fix. Intent-driven bins name each scenario, and the deep bin mechanics live in the Bins topic.
64 bins holding 4 values each. Per-value resolution is gone, so boundary checks need explicit bins.
auto_bin_max the wrong fix?Almost always: it creates hundreds of unnameable bins nobody can review or close. Explicit intent-driven bins stay reviewable.
The bench counts the alignment and 4 KB-crossing coverpoints from this lesson, then the same five lengths into auto bins and into planned bins.
// uses: covmodel
// Expression coverpoints, and what auto bins do to an 8-bit length.
module tb_cov_expr;
cm_point cp_align = new("cp_align (addr[1:0])");
cm_point cp_4k = new("cp_cross_4k");
cm_point auto_len = new("cp_len_auto");
cm_point plan_len = new("cp_len_plan");
initial begin
cp_align.add("word_aligned", 0, 0); cp_align.add("half_aligned", 2, 2);
cp_align.add_values("unaligned[1]", '{1}); cp_align.add_values("unaligned[3]", '{3});
cp_4k.add("stays_inside", 0, 0); cp_4k.add("crosses_boundary", 1, 1);
// No bins: auto bins, capped at auto_bin_max = 64, so 4 values per bin.
auto_len.add_split("auto", 0, 255, 64);
plan_len.add("zero", 0, 0); plan_len.add("one", 1, 1); plan_len.add("mid", 2, 254); plan_len.add("max_len", 255, 255);
for (int a = 0; a < 16; a++) cp_align.sample(a & 3);
cp_4k.sample((4090 % 4096) + 8 > 4096); // a burst of 8 at 4090 crosses
cp_4k.sample((4000 % 4096) + 8 > 4096);
// Lengths the stimulus produced: never 255, but 252 lands in the same auto bin.
begin
int lens[5] = '{0, 1, 2, 100, 252};
foreach (lens[i]) begin auto_len.sample(lens[i]); plan_len.sample(lens[i]); end
end
cp_align.report(); cp_4k.report();
$display(" auto bin [63] holds 252 to 255: %0d hit, so it reads covered although 255 never ran", auto_len.bin_list[63].hits);
plan_len.report();
if (cp_align.bin_list[0].hits != 4 || cp_align.bin_list[2].hits != 4 || cp_4k.n_covered() != 2) $fatal(1, "FAIL: expression bins");
if (auto_len.bin_list[63].hits != 1 || plan_len.bin_list[3].hits != 0) $fatal(1, "FAIL: bucketing");
$display("PASS: 16 addresses sorted into alignment bins, and the auto bins hid the missing 255 that the planned bins exposed");
$finish;
end
endmodule coverpoint cp_align (addr[1:0]): 100.00% (4 of 4 bins)
word_aligned 4
half_aligned 4
unaligned[1] 4
unaligned[3] 4
coverpoint cp_cross_4k: 100.00% (2 of 2 bins)
stays_inside 1
crosses_boundary 1
auto bin [63] holds 252 to 255: 1 hit, so it reads covered although 255 never ran
coverpoint cp_len_plan: 75.00% (3 of 4 bins) below goal 100
zero 1
one 1
mid 3
max_len 0 <- hole
PASS: 16 addresses sorted into alignment bins, and the auto bins hid the missing 255 that the planned bins exposedCounted with the course's bins model (rich/coverage/cov_model.sv, listed on the Coverage hub) on Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator 5.048 parses covergroups with a COVERIGN warning and ignores their bins, and Icarus 13 has none, so each covergroup listing is marked not run and its bench counts the same bins with the model.
16 addresses sorted into alignment bins; the auto bins read full with 255 missing, and the planned bins named it.