A coverage plan is a systematic transcription of the spec into measurable scenarios. Four extraction questions produce the feature list: what values each field takes, what combinations the logic acts on together, what sequences must occur in order, and what boundaries the text calls out.
Spec sentence | Coverable feature |
|---|---|
“Transfers of 1 to 16 beats are supported” | Length bins 1, [2:15], 16, with both boundaries named |
“WRAP bursts must be 2, 4, 8, 16” |
|
“A timeout aborts the transfer and returns the FSM to IDLE” | Transition bin XFER => IDLE on timeout |
“QoS affects arbitration only when two masters contend” | Cross of QoS by grant, gated by contention |
“The FIFO asserts full at 64” | Occupancy bins 0, 63, 64: the boundary and both sides |
Numbers in spec prose are coverage gold. Every explicit constant is a boundary the designers special-cased, and boundaries are where bugs live.
The plan is a table, one row per feature, written before the covergroups. Each row carries enough to implement the measurement and enough to audit it later.
ID | Feature (spec ref) | Coverpoint or cross | Bins | Priority |
|---|---|---|---|---|
CV_LEN | Burst lengths (s7.2) |
| 1, [2:15], 16 | P1 |
CV_WRP | Wrap legality (s7.2.1) |
| illegal: wrap × 1 | P1 |
CV_TMO | Timeout recovery (s9.4) |
| XFER => IDLE | P1 |
CV_QOS | QoS under contention (s8.1) |
| 8 named cells | P2 |
CV_OCC | FIFO boundaries (s5.3) |
| 0, 63, 64 | P1 |
CV_RSV | Reserved encodings (s3.9) |
| ignore: [12:15] | P3 |
The ID is a stable handle that code comments and reports refer to. The feature column is one sentence plus the spec section it transcribes; the coverpoint column names the construct that implements it; the bins column lists the buckets, including planned illegal and ignore bins; and the priority drives weights, closing order and waiver scrutiny.
Plan IDs are stable and boring: CV_LEN style, never renumbered, only appended.
Covergroup elements carry the ID in a comment on the same line as the bin they implement.
option.comment on the covergroup names the plan section it serves, so the report self-documents.
Bin names echo the feature, not the value: timeout_recovery, not b3. Reports are read by people who did not write the code.
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.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
interface dma_cov_if(input logic clk);
logic desc_done;
logic [1:0] btype; // 0 INCR, 1 FIXED, 2 WRAP
logic [4:0] blen; // 1..16
covergroup dma_cg @(posedge clk iff desc_done);
option.comment = "coverage plan CV_LEN, CV_WRP — spec s7.2";
cp_btype : coverpoint btype {
bins incr = {0};
bins fixed = {1};
bins wrap = {2};
}
cp_len : coverpoint blen { // CV_LEN
bins one = {1}; // CV_LEN: lower boundary
bins mid = {[2:15]}; // CV_LEN: interior
bins max_len = {16}; // CV_LEN: upper boundary
}
x_wrap : cross cp_btype, cp_len {
illegal_bins wrap_bad = binsof(cp_btype.wrap) &&
binsof(cp_len.one); // CV_WRP: spec s7.2.1
}
endgroup
endinterfaceThe plan is reviewed as a document, with the spec open, before implementation. Reviewing a finished covergroup conflates intent with code, and intent mistakes found late invalidate regression data already collected.
Walk the spec section by section: does every must sentence have a plan row or a written reason it does not?
Are boundary values from spec prose present as named bins, not buried in ranges?
Is every cross justified by logic that actually examines the fields together?
Are priorities assigned, and do P1 rows cover the features whose failure is unshippable?
Are planned illegal and ignore bins listed up front, not improvised at 87%?
Can each row be measured from observable signals, or must instrumentation be requested now?
Three named bins: 1, [2:15], and 16. Both boundaries get their own evidence instead of hiding inside one range.
So review can verify each requirement sentence has a row, and sign-off can audit each number. Rows without references are unverifiable.
The bench builds plan rows CV_LEN and CV_WRP with the bins model, next to the same length field written as one range bin, and runs a regression that sends every burst type with lengths 2 to 15 only.
cp_len and the cross read?The range bin reads 100%: every value from 2 to 15 lands in it. cp_len reads 1 of 3 and names one and max_len as holes. The cross reads 3 of 8: WRAP × one is illegal and leaves the goal, and the five other boundary cells are holes.
// uses: covmodel
// Plan rows CV_LEN and CV_WRP as bins, against a single range bin, over a regression that never sends 1 or 16 beats.
module tb_dma_plan;
cm_point cp_btype = new("cp_btype"), cp_len = new("cp_len (CV_LEN)"), cp_range = new("cp_len (one range bin)");
cm_cross x_wrap;
initial begin
cp_btype.add("incr", 0, 0); cp_btype.add("fixed", 1, 1); cp_btype.add("wrap", 2, 2);
cp_len.add("one", 1, 1); // CV_LEN: lower boundary
cp_len.add("mid", 2, 15); // CV_LEN: interior
cp_len.add("max_len", 16, 16); // CV_LEN: upper boundary
cp_range.add("len", 1, 16); // "1 to 16 beats", transcribed as one range
x_wrap = new("x_wrap (CV_WRP)", cp_btype, cp_len);
x_wrap.illegal(x_wrap.sel("wrap", "one")); // CV_WRP: spec s7.2.1
// The regression: every burst type, lengths 2 to 15 only.
for (int t = 0; t < 3; t++)
for (int len = 2; len <= 15; len++) begin
cp_btype.sample(t); cp_len.sample(len); cp_range.sample(len); x_wrap.sample();
end
cp_range.report(); cp_len.report(); x_wrap.report();
if (cp_range.get_coverage() != 100.0 || cp_len.n_covered() != 1 || x_wrap.n_bins() != 8 || x_wrap.n_covered() != 3)
$fatal(1, "FAIL: plan bins");
$display("PASS: the range bin read 100%%; the planned bins named both untested boundaries");
$finish;
end
endmodule coverpoint cp_len (one range bin): 100.00% (1 of 1 bins)
len 42
coverpoint cp_len (CV_LEN): 33.33% (1 of 3 bins) below goal 100
one 0 <- hole
mid 42
max_len 0 <- hole
cross x_wrap (CV_WRP): 37.50% (3 of 8 bins)
hole: <incr, one>
hole: <incr, max_len>
hole: <fixed, one>
hole: <fixed, max_len>
hole: <wrap, max_len>
PASS: the range bin read 100%; the planned bins named both untested boundariesCounted 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.
With lengths 2 to 15 only, the range bin read 100% and the planned bins read 1 of 3, naming one and max_len.