Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
    • Covergroups & Coverpoints
      • Covergroup Anatomy
      • Sampling: Clocked, Event & sample()
      • Coverpoint Expressions & Types
      • Building Your First Coverage Model
    • Bins: Explicit, Auto, Illegal & Ignore
    • Cross Coverage
    • Coverage Options & Methods
    • Coverage Closure & Sign-Off
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Covergroups & Coverpoints
  1. Topics
  2. SystemVerilog
  3. Functional Coverage in SystemVerilog
  4. Covergroups & Coverpoints
  5. Coverpoint Expressions & Types

Part 5 · Functional Coverage · Intermediate

Coverpoint Expressions & Types

Expression coverpoints, enum auto-bins, boolean idioms and the auto_bin_max cap.

On this page

    Why this matters

    A coverpoint on a raw 8-bit address read 100% while the one address the plan cared about, 255, was never sent. Auto bins had put it in a bucket with three neighbours that were.

    After this lesson you can: cover an expression or property of a signal rather than the raw value, use enums and conditions, and know when auto bins bucket values.

    Cover the property

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 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
    endclass

    The expression sees the sample() arguments exactly at the sampling instant. Keep expressions side-effect-free: a stateful function call makes sampling order load-bearing.

    Enums and conditions

    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.

    fsm_cov.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 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();
    endmodule

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

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 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
    endclass

    Auto bins have a cap

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

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 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
    endclass

    Raising the cap is rarely the fix. Intent-driven bins name each scenario, and the deep bin mechanics live in the Bins topic.

    Common pitfall

    Auto bins on any field wider than 6 bits quietly bucket values into ranges. Boundary values share bins with neighbors, so exactly the cases the plan cares about disappear from the report.

    Interview

    “Coverage dropped after an RTL enum change. Why?”

    A new literal adds a new automatic bin, which starts unhit. That drop is the tool doing its job: exercise the new state and close it.

    Check yourself An 8-bit length field uses auto bins. How many bins appear, and what does each hold?

    64 bins holding 4 values each. Per-value resolution is gone, so boundary checks need explicit bins.

    Check yourself When is raising 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.

    Testbench

    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.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 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
    Terminal
      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 exposed

    Remember

    • Cover the property the plan names: alignment, not the address.

    • Enum coverpoints get one bin per named value.

    • Above 64 values, auto bins share buckets.

    Rule of thumb: if the plan names a value, give it its own bin.

    How this was checked

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

    Related topics

    • Auto vs explicit bins

    Previous

    143 / 259

    Next: Building Your First Coverage Model

    Up next

    Up nextBuilding Your First Coverage Model

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Covergroup Anatomy