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
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
    • Reusable Testbench Architecture
    • Checking Out-of-Order Traffic
    • Assertion Depth
    • Coverage Modelling for Real Features
      • Transaction and Temporal Coverage
      • Coverage Filters
      • Assertion-Backed Coverage
      • Closure at Scale
    • Capstone: Out-of-Order Memory Controller
    • Principal Judgment
Loading lesson

‹Coverage Modelling for Real Features
  1. Topics
  2. SystemVerilog
  3. Principal-Level Verification
  4. Coverage Modelling for Real Features
  5. Coverage Filters

Part 9 · Principal-Level Verification · Advanced

Coverage Filters

Filter bins and crosses with with-clauses and cross functions, and check the legal count against a hand model.

On this page

    A cross is only useful if every bin can be hit

    A full cross of burst length, beat size and burst type is 5 x 4 x 2 = 40 tuples. Some of them break the spec. If they stay in the cross, the model can never reach 100%, and engineers learn to ignore the number. If they are removed by hand-written binsof lists, the lists drift from the spec. Filters state the rule once, as an expression.

    Exclusions, patterns and filters and pruning crosses introduce the syntax. This lesson is about using it for a real rule set and proving the count.

    The rules for this bus

    Rule

    Source

    Treatment

    Length is 1, 2, 4, 8 or 16 beats

    Spec

    Coverpoint with filter: only powers of two become bins.

    A burst moves at most 64 bytes

    Spec

    illegal_bins: hitting it is a stimulus or DUT bug and must error.

    WRAP of length 1

    Spec allows it, design treats it as INCR

    ignore_bins: legal, but not a distinct feature. The reason goes in the plan.

    The split between ignore and illegal is a statement about the spec, not about which bins are hard to hit. Illegal means "must never happen" and the tool reports an error when it does. Ignore means "may happen, not worth a 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
    // not-run: covergroups are parsed but ignored by Verilator 5.048 (COVERIGN warnings, get_coverage() returns 0) and rejected by Icarus 13; needs a commercial simulator
    typedef enum bit {INCR, WRAP} burst_e;
    
    class burst_cov;
      covergroup cg with function sample(int unsigned len, int unsigned size, burst_e burst);
        cp_len: coverpoint len {
          bins len[] = {[1:16]} with ($countones(item) == 1);   // 1, 2, 4, 8, 16
        }
        cp_size: coverpoint size {
          bins size[] = {[0:3]};                                 // 2**size bytes per beat
        }
        cp_burst: coverpoint burst {
          bins incr = {INCR};
          bins wrap = {WRAP};
        }
        x_lsb: cross cp_len, cp_size, cp_burst {
          illegal_bins too_big = x_lsb with ((cp_len << cp_size) > 64);
          ignore_bins  wrap_1  = binsof(cp_burst.wrap) && binsof(cp_len) intersect {1};
        }
      endgroup
    
      function new();
        cg = new();
      endfunction
    endclass

    How the cross with-clause is evaluated

    Inside a cross, the with expression uses the coverpoint names as the values of a candidate tuple. Each bin tuple stands for a set of value tuples, one per combination of the values its bins contain. A bin tuple is selected when enough of its value tuples make the expression true. "Enough" is set by matches: the default is 1 (any value tuple), and matches $ means all of them (IEEE 1800-2017, 19.6.1.2).

    This matters as soon as a bin holds more than one value. Suppose size had a bin big = {[2:3]}. For length 16, the value tuple (16, 2) is 64 bytes, legal, and (16, 3) is 128 bytes, illegal. With the default policy the whole tuple (len16, big) is selected as illegal, so a legal 64-byte burst raises an error. With matches $ it is not selected, and the 128-byte case is not caught. Neither is right. Give the coverpoints one value per bin wherever a cross filter depends on the value, as the model above does.

    Cross functions

    When a rule is easier as a loop than as an expression, a function declared in the cross body can return the tuples. Its return type is the cross's own CrossQueueType, a queue of CrossValType structs with one field per coverpoint, holding values. This form has been in the standard since 1800-2012; support across simulators has lagged, so try it on your tool before building a plan on it.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet: the too_big rule as a cross function (inside the cross body of x_lsb)
    function CrossQueueType too_big_tuples();
      for (int l = 1; l <= 16; l *= 2)
        for (int s = 0; s < 4; s++)
          if ((l << s) > 64) begin
            too_big_tuples.push_back('{l, s, INCR});
            too_big_tuples.push_back('{l, s, WRAP});
          end
    endfunction
    illegal_bins too_big = too_big_tuples();

    Checking the filter against a hand count

    A filter is code, so test it. Count the expected result by hand from the rules, then make the model agree. Of the 40 tuples, too-big is length 16 with 8-byte beats, for both burst types: 2 tuples. WRAP of length 1 is one per size: 4 tuples. Legal is 40 - 2 - 4 = 34.

    The bench below is a procedural mirror of the covergroup, because the free tools cannot collect it. The module classifies each sample the way the bins and filters do. The testbench drives every value of every coverpoint, crossed, and checks the counts. It then runs a stream that never uses 8-byte beats and checks that coverage stops at 27 of 34, since the seven legal 8-byte tuples are missing.

    burst_bin.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
    // Procedural mirror of the filtered cross len x size x burst.
    // Returns 0 = legal bin, 1 = ignored (WRAP of length 1), 2 = illegal (> 64 bytes),
    // 3 = not a bin at all (length not a power of two).
    module burst_bin (
      input  logic [4:0] len,     // 1..16 beats
      input  logic [1:0] size,    // 2**size bytes per beat
      input  logic       wrap,    // 0 = INCR, 1 = WRAP
      output logic [1:0] kind,
      output logic [5:0] idx      // 0..39: position in the full 5 x 4 x 2 cross
    );
      timeunit 1ns; timeprecision 1ps;
      logic [2:0] len_bin;
      always_comb begin
        case (len)
          5'd1: len_bin = 3'd0;  5'd2: len_bin = 3'd1;  5'd4: len_bin = 3'd2;
          5'd8: len_bin = 3'd3;  5'd16: len_bin = 3'd4; default: len_bin = 3'd7;
        endcase
        idx = 6'(len_bin) * 6'd8 + 6'(size) * 6'd2 + 6'(wrap);
        if (len_bin == 3'd7)                       kind = 2'd3;
        else if ((32'(len) << size) > 32'd64)      kind = 2'd2;   // illegal_bins: too_big
        else if (wrap && len == 5'd1)              kind = 2'd1;   // ignore_bins: wrap_1
        else                                       kind = 2'd0;
      end
    endmodule
    

    Changing > 64 to >= 64 in the classifier, a one-character slip in the filter, moves the 64-byte bursts (length 16 with 4-byte beats and length 8 with 8-byte beats) into illegal. The counts become 30 legal and 6 illegal and the bench fails. On a real tool that slip would raise errors on legal traffic, or worse, if the rule were an ignore_bins, quietly drop four legal bins from the goal.

    Common pitfall

    Writing a filter from what the DUT does today. If the design wrongly rejects 64-byte bursts and the filter is copied from the RTL, the coverage model agrees with the bug. Filters come from the spec, and each one cites the clause it implements.

    Interview

    Question: "When do you use ignore_bins and when illegal_bins in a cross?" Answer: illegal when the spec says the combination must never occur, so a hit is a bug and should error. Ignore when it can occur but is not a separate feature. Never use ignore to hide a combination that is legal but hard to reach; that is a hole, and it goes in the hole list with a plan.

    Check yourself A cross bin tuple contains a size bin {[2:3]}. The with-filter marks the tuple illegal for length 16. What went wrong?

    The default matches policy is 1, so one illegal value tuple (16, 3) selected the whole bin tuple, including the legal (16, 2). Split the bin into single values, or the filter cannot tell them apart.

    Check yourself Why check the legal-tuple count against a hand count instead of trusting the tool's report?

    The tool reports what the model says, including the model's mistakes. A count derived independently from the spec rules (34 here) catches a wrong operator or a missing rule before the number is used for sign-off.

    Related topics

    • Previous: Transaction and Temporal Coverage

    • Next: Assertion-Backed Coverage

    • Pruning Crosses: ignore_bins & illegal_bins

    Previous

    247 / 259

    Next: Assertion-Backed Coverage

    Up next

    Up nextAssertion-Backed Coverage

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Closure at Scale