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.
Rule | Source | Treatment |
|---|---|---|
Length is 1, 2, 4, 8 or 16 beats | Spec | Coverpoint |
A burst moves at most 64 bytes | Spec |
|
WRAP of length 1 | Spec allows it, design treats it as INCR |
|
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".
// 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
endclassInside 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.
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.
// 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();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.
// 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.
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.
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.