Part 5 · Functional Coverage · Intermediate
Coverpoint Expressions & Types
Covering expressions not just signals, enum coverage with automatic named bins, boolean condition idioms, and auto bin counts with auto_bin_max.
Coverpoints watch expressions, not just signals
A coverpoint's target is any integral expression, evaluated at the sampling moment. That means you can cover derived properties directly — alignment, relationships between fields, decoded conditions — instead of covering raw values and reconstructing the scenario in your head while reading the report.
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};
}
endgroupThe expression is evaluated exactly when the covergroup samples — using the values of the sample() arguments or scope variables at that instant. Keep expressions side-effect-free; a coverpoint expression that calls a function with state changes is a debugging nightmare.
Enum coverage: automatic named bins
Coverpoints on enum variables are a free gift: the simulator automatically creates one named bin per enumeration literal. State machines, opcode sets, and response codes get readable, complete coverage with a one-line coverpoint — and when someone adds a new enum value, the new bin appears automatically and shows up as a hole until it is exercised.
typedef enum logic [2:0] {
IDLE, FETCH, DECODE, EXEC, MEM, WB
} state_e;
covergroup cg_fsm @(posedge clk);
// One bin per literal: IDLE, FETCH, DECODE, EXEC, MEM, WB
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};
}
endgroupBoolean and condition idioms
A 1-bit coverpoint gets two auto bins (0 and 1), which already proves both outcomes occurred. For report readability, name them. A common idiom set:
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 (wr_ptr < rd_ptr) { bins wrapped = {1};
bins linear = {0}; }Auto bins and auto_bin_max
When a coverpoint has no explicit bins, the simulator creates automatic bins from the expression's type. For an N-bit expression there are 2^N possible values — but the LRM caps auto-bin creation at auto_bin_max, which defaults to 64 . If 2^N exceeds the cap, values are silently distributed across 64 equal-range buckets. An 8-bit field gets 64 bins of 4 values each; a 32-bit field gets 64 enormous ranges. The report looks plausible while measuring almost nothing specific.
AUTO BIN COUNT (no explicit bins)
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/bin
16 bits 65536 64 ◄── 1024 values/bin
enum (6 literals) 6 6 named bins (special rule)
auto_bin_max default = 64
→ any expression wider than 6 bits silently loses per-value resolutioncovergroup 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 (see Bins topic)
cp_len_plan : coverpoint len {
bins zero = {0};
bins one = {1};
bins mid = {[2:254]};
bins max_len = {255};
}
endgroupKey takeaways
Coverpoints accept any integral expression — cover the property (alignment, boundary-cross), not just the raw signal.
Enum coverpoints get one automatic named bin per literal — the cheapest complete FSM-state coverage available.
1-bit condition coverpoints prove both outcomes occurred; name the bins for readable reports.
auto_bin_max defaults to 64 — any auto-binned expression wider than 6 bits is silently range-bucketed.
Common pitfalls
Auto bins on an 8-bit-or-wider field — 64 range buckets that hide exactly the boundary values you care about.
Raising auto_bin_max instead of writing explicit bins — thousands of unnameable bins nobody can close or review.
Coverpoint expressions with side effects — sampling order becomes load-bearing and unportable.
Forgetting that adding an enum literal adds a bin — coverage drops after an RTL enum change and nobody knows why (this one is actually a feature; recognize it).