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
    • Bins: Explicit, Auto, Illegal & Ignore
      • Auto Bins vs Explicit Bins
      • Transition Bins
      • Exclusions, Patterns & Filters
      • Bin Design Strategy
    • Cross Coverage
    • Coverage Options & Methods
    • Coverage Closure & Sign-Off
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Bins: Explicit, Auto, Illegal & Ignore
  1. Topics
  2. SystemVerilog
  3. Functional Coverage in SystemVerilog
  4. Bins: Explicit, Auto, Illegal & Ignore
  5. Auto Bins vs Explicit Bins

Part 5 · Functional Coverage · Intermediate

Auto Bins vs Explicit Bins

Auto bins cap at 64 buckets; explicit bins name each scenario the plan requires.

On this page

    Why this matters

    Length 0 and length 3 shared auto[0] on an 8-bit field, so the zero-length bug that only 0 triggers hid behind hits on 1, 2 and 3.

    After this lesson you can: predict what auto bins do to a field, write explicit value and range bins, and choose between one bin, b[] and b[N].

    What auto bins actually do

    A coverpoint with no bin declarations gets one bin per value, up to a cap. The cap is option.auto_bin_max, and its default is 64.

    Wider value spaces are dealt evenly across that many buckets, with any remainder in the last bucket. The simulator gives no warning by default.

    coverpoint len; on bit [7:0] len

    What you get

    What you think you get

    256 bins, one per value

    What you actually get

    64 bins of 4 values: auto[0] = 0 to 3, ..., auto[63] = 252 to 255

    Consequence

    len == 0 and len == 3 are the same bin; a hit on auto[0] may have been len == 2 every time

    Wider fields

    bit [31:0] gives 64 buckets of about 67 million values each

    Auto bins suit narrow fields. A 4-bit opcode gets 16 per-value bins, and an enum coverpoint gets one named bin per literal.

    Check yourself An 8-bit length coverpoint has no explicit bins. How many bins exist, and what shares a bin with len==0?

    64 bins. Values 0, 1, 2 and 3 share auto[0], so len==0 is unverifiable on its own.

    Explicit value and range bins

    Explicit bins replace the carve-up with named, plan-driven buckets. The listing below is not run in this course's checks: the open simulators do not count covergroups, so the lesson's bench counts it with the bins model.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup cg with function sample(bit [7:0] len);
      cp_len : coverpoint len {
        bins zero      = {0};                 // single value
        bins one       = {1};
        bins small     = {[2:8]};             // one bin for the range
        bins medium    = {[9:64]};
        bins large     = {[65:254]};
        bins max_len   = {255};               // boundary as its own bin
        bins odd_picks = {3, 17, 129};        // list → still ONE bin
      }
    endgroup
    // 7 goal bins. Hitting ANY value in [2:8] covers "small".
    // The report reads like the verification plan.

    Each bin is one plan scenario. Hitting any value in [2:8] covers small, and the report reads like the plan.

    Check yourself The bin odd_picks holds {3, 17, 129}. Which samples cover it, and how many goal bins does it add?

    Any of the three values covers it. It adds exactly one goal bin.

    One bin or many

    Appending [] turns one bin into an array with one element per value in the set. Appending [N] creates exactly N bins and distributes the values across them.

    The listing below is not run in this course's checks: the open simulators do not count covergroups, so the lesson's bench counts it with the bins model.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
    covergroup cg with function sample(bit [7:0] len);
      // ONE bin — any value in 0..15 covers it
      cp_a : coverpoint len { bins any_low      = {[0:15]}; }
    
      // SIXTEEN bins — every value 0..15 must be hit individually
      cp_b : coverpoint len { bins each_low[]   = {[0:15]}; }
    
      // FOUR bins — 16 values distributed 4 per bin:
      // each_q[0]={0..3} each_q[1]={4..7} each_q[2]={8..11} each_q[3]={12..15}
      cp_c : coverpoint len { bins each_q[4]    = {[0:15]}; }
    endgroup

    Declaration

    Bins

    It proves

    Choose it when

    bins b = {[0:15]};

    1

    The range was touched

    Entering the range at all is the scenario

    bins b[] = {[0:15]};

    16

    Every value was seen

    Every value matters (mind the explosion)

    bins b[4] = {[0:15]};

    4

    Every quarter was seen

    You want deliberate resolution between extremes

    Interview

    The standard screen asks the difference between bins b = {[0:15]} and bins b[] = {[0:15]}. Answer with goal-bin counts: 1 versus 16.

    Check yourself When is bins b[] over a wide range the wrong choice?

    When each value is not a distinct plan item. It is self-inflicted bin explosion that makes closure impossible.

    The b[N] split math

    A range dealt across bins b[N] divides evenly when it can. When the value count does not divide evenly, the last bin takes the remainder.

    Declaration

    Split

    bins q[4] = {[0:15]};

    16 values over 4 bins: q[0] = 0 to 3, q[1] = 4 to 7, q[2] = 8 to 11, q[3] = 12 to 15

    bins t[3] = {[0:9]};

    10 values over 3 bins, remainder 1: t[0] = 0 to 2, t[1] = 3 to 5, t[2] = 6 to 9 (four values)

    bins h[4] = {[0:255]};

    64 each: h[0] = 0 to 63, ..., h[3] = 192 to 255

    bins m[2] = {[0:3],[8:11]};

    Ranges pooled first, then dealt: m[0] = 0 to 3, m[1] = 8 to 11

    The m[2] alignment on range boundaries is coincidental. Uneven multi-range sets pool values first, so verify uneven splits instead of relying on range boundaries.

    Setting N above the value count leaves extra bins uncreated, like b[]. Most tools warn; treat the warning as a design smell.

    Common pitfall

    Trusting auto bins on an 8-bit-plus field buries boundary values in multi-value buckets. Fixing it with bins b[] over the whole range repeats the trap by hand.

    Testbench

    The bench counts this lesson's bins with the bins model: the seven named length bins, then one range bin, a per-value array and a four-way split, all fed the same eight values.

    Predict first Eight values reach any_low, each_low[] and each_q[4], six of them inside 0 to 15. What does each report?

    any_low 1 of 1, because any value in the range covers it. each_q[4] 4 of 4, because the six values happen to land in all four quarters. each_low[] only 6 of 16, because each value is its own bin.

    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
    32
    // uses: covmodel
    // Explicit bins, one bin versus many, and the [N] split, counted with the bins model.
    module tb_bins_explicit;
      cm_point cp_len = new("cp_len"), cp_a = new("cp_a"), cp_b = new("cp_b"), cp_c = new("cp_c"), cp_t = new("cp_t");
      initial begin
        // bins zero = {0}; one = {1}; small = {[2:8]}; medium = {[9:64]}; large = {[65:254]};
        // max_len = {255}; odd_picks = {3, 17, 129};
        cp_len.add("zero", 0, 0); cp_len.add("one", 1, 1); cp_len.add("small", 2, 8);
        cp_len.add("medium", 9, 64); cp_len.add("large", 65, 254); cp_len.add("max_len", 255, 255);
        cp_len.add_values("odd_picks", '{3, 17, 129});
        cp_a.add("any_low", 0, 15);              // bins any_low      = {[0:15]};
        cp_b.add_each("each_low", 0, 15);        // bins each_low[]   = {[0:15]};
        cp_c.add_split("each_q", 0, 15, 4);      // bins each_q[4]    = {[0:15]};
        cp_t.add_split("t", 0, 9, 3);            // bins t[3]         = {[0:9]};
        // The same eight values reach every coverpoint.
        begin
          int vals[8] = '{0, 1, 3, 5, 9, 12, 70, 255};
          foreach (vals[i]) begin
            cp_len.sample(vals[i]); cp_a.sample(vals[i]); cp_b.sample(vals[i]); cp_c.sample(vals[i]);
            if (vals[i] <= 9) cp_t.sample(vals[i]);
          end
        end
        cp_len.report(); cp_a.report(); cp_c.report();
        $display("  coverpoint cp_b (each_low[]): %0.2f%% (%0d of 16 bins)", cp_b.get_coverage(), cp_b.n_covered());
        cp_t.report();
        $display("  t[2] holds %0d values; t[0] and t[1] hold %0d each", cp_t.bin_list[2].vals.size(), cp_t.bin_list[0].vals.size());
        if (cp_len.get_coverage() != 100.0 || cp_a.n_covered() != 1 || cp_b.n_covered() != 6 || cp_c.n_covered() != 4) $fatal(1, "FAIL: bin counts");
        if (cp_t.bin_list[2].vals.size() != 4) $fatal(1, "FAIL: split remainder");
        $display("PASS: the same samples covered 1 of 1, 6 of 16 and 4 of 4 bins, and t[2] took the remainder");
        $finish;
      end
    endmodule
    Terminal
      coverpoint cp_len: 100.00% (7 of 7 bins)
        zero           1
        one            1
        small          2
        medium         2
        large          1
        max_len        1
        odd_picks      1
      coverpoint cp_a: 100.00% (1 of 1 bins)
        any_low        6
      coverpoint cp_c: 100.00% (4 of 4 bins)
        each_q[0]      3
        each_q[1]      1
        each_q[2]      1
        each_q[3]      1
      coverpoint cp_b (each_low[]): 37.50% (6 of 16 bins)
      coverpoint cp_t: 100.00% (3 of 3 bins)
        t[0]           2
        t[1]           2
        t[2]           1
      t[2] holds 4 values; t[0] and t[1] hold 3 each
    PASS: the same samples covered 1 of 1, 6 of 16 and 4 of 4 bins, and t[2] took the remainder
    Check yourself bins t[3] = {[0:9]}: which bin holds four values?

    t[2] = {6..9}. Ten values over three bins leaves a remainder of one, and the last bin takes it.

    Remember

    • auto_bin_max is 64: wider fields share buckets.

    • b = {[0:15]} is one bin; b[] is one per value; b[N] splits.

    • b[N] gives any remainder to the last bin.

    Rule of thumb: name the boundary values; bucket only the uniform middle.

    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.

    The same eight values covered 1 of 1, 6 of 16 and 4 of 4 bins in the three forms, and t[3] = {[0:9]} put four values in t[2].

    Related topics

    • Coverpoint expressions and auto_bin_max

    Previous

    145 / 259

    Next: Transition Bins

    Up next

    Up nextTransition Bins

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Exclusions, Patterns & Filters
    • Bin Design Strategy