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.

systemverilog
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

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

systemverilog
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};
  }
endgroup

Boolean 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:

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

diagram
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 resolution
systemverilog
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 (see Bins topic)
  cp_len_plan : coverpoint len {
    bins zero    = {0};
    bins one     = {1};
    bins mid     = {[2:254]};
    bins max_len = {255};
  }
endgroup

Key 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).