Value bins prove a state was visited. Transition bins prove a path was taken: bins b = (A => B) hits when consecutive samples of the coverpoint were A then B.
Consecutive means consecutive samples of the covergroup. Disciplined sampling at every relevant moment is what keeps that word honest.
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
typedef enum logic [1:0] { IDLE, BUSY, DONE, ERR } st_e;
covergroup cg_fsm @(posedge clk);
cp_arc : coverpoint state iff (rst_n) {
// single transitions (FSM arcs)
bins idle_busy = (IDLE => BUSY);
bins busy_done = (BUSY => DONE);
bins done_idle = (DONE => IDLE);
bins any_err = (IDLE, BUSY, DONE => ERR); // set → value
// multi-step path: full happy loop in one bin
bins happy_loop = (IDLE => BUSY => DONE => IDLE);
// multiple transitions in one bin (either counts)
bins recover = (ERR => IDLE), (ERR => BUSY);
}
endgroupFour consecutive samples reading IDLE, BUSY, DONE, IDLE in that order.
A [N] repeats a value for exactly N consecutive samples, with [n:m] giving a range. A [->N] is the nonconsecutive goto: N occurrences with other values allowed in between.
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
covergroup cg_seq @(posedge clk);
cp_st : coverpoint state iff (rst_n) {
// BUSY held exactly 3 cycles between IDLE and DONE:
bins busy3 = (IDLE => BUSY [*3] => DONE);
// BUSY held 2 to 4 cycles:
bins busy_short = (IDLE => BUSY [*2:4] => DONE);
// 2 non-consecutive ERR visits, then recovery to IDLE:
bins flaky = (ERR [->2] => IDLE);
}
endgroupSample stream | Bin | Why it hits |
|---|---|---|
IDLE BUSY BUSY BUSY DONE IDLE |
| IDLE, then BUSY on three consecutive samples, then DONE |
ERR IDLE BUSY ERR IDLE |
| Two ERRs with gaps allowed, and the sample after the second is IDLE |
Yes. Two ERR occurrences arrive with gaps allowed, and the next sample is IDLE.
Watch the middle row, state. Encoded values are 0 IDLE, 1 BUSY, 2 DONE, with rst_n gating sampling through iff.
Slot 0: state is 0 (IDLE) and rst_n is 1. A path opens.
Slot 1: state is 1 (BUSY).
Slot 2: state is still 1.
Slot 3: state is still 1, the third consecutive BUSY.
Slot 4: state is 2 (DONE). The chain IDLE, BUSY x3, DONE is complete.
Slot 5: state is 0 (IDLE). A new path opens.
Slot 6: state is 1 (BUSY).
Slot 7: rst_n is 0, so iff skips this sample. What happens to the in-flight path is not fixed by the standard.
The stream above is simulated stimulus. The busy3 hit at slot 4 follows from the transition rules.
IEEE 1800 does not say whether a false iff aborts an in-flight transition or simply skips the sample. Many tools skip, so a sequence can be counted across a reset gap. To force an abort, fold reset into the expression, for example coverpoint (rst_n ? state : RESET_VAL), or use an SVA cover property.
No, under either reading. If the skip aborts the path, the chain is gone. If the skip is ignored, only one BUSY (slot 6) precedes the DONE, not three.
The standard FSM recipe pairs one enum coverpoint for state occupancy with one transition coverpoint for every legal arc from the state diagram. Together they prove every state was reached and every designed edge was taken.
Transition bins watch one coverpoint over consecutive samples. Multi-signal relations and elastic timing belong to cover property with SVA sequences.
Rule of thumb: same variable with consecutive samples means transition bins, while multiple signals or elastic timing means SVA cover.
Do not cross transition bins. Cross value bins instead, and select combinations with binsof.
The bench counts both coverpoints from this lesson over the two sample streams in the table, using the model's transition bins: also() adds an alternative to a bin, as a comma does in bins recover = (ERR => IDLE), (ERR => BUSY);, and add_goto() stands for [->N].
// uses: covmodel
// The FSM transition bins from this lesson, counted over the lesson's sample streams.
typedef enum int {IDLE, BUSY, DONE, ERR} st_e;
module tb_trans_bins;
cm_point cp_arc = new("cp_arc"), cp_st = new("cp_st");
initial begin
cp_arc.add_trans("idle_busy", '{IDLE, BUSY});
cp_arc.add_trans("busy_done", '{BUSY, DONE});
cp_arc.add_trans("done_idle", '{DONE, IDLE});
cp_arc.add_trans("any_err", '{IDLE, ERR}); cp_arc.also('{BUSY, ERR}); cp_arc.also('{DONE, ERR}); // (IDLE, BUSY, DONE => ERR)
cp_arc.add_trans("happy_loop", '{IDLE, BUSY, DONE, IDLE});
cp_arc.add_trans("recover", '{ERR, IDLE}); cp_arc.also('{ERR, BUSY}); // (ERR => IDLE), (ERR => BUSY)
cp_st.add_trans("busy3", '{IDLE, BUSY, BUSY, BUSY, DONE}); // (IDLE => BUSY [*3] => DONE)
cp_st.add_trans("busy_short", '{IDLE, BUSY, BUSY, DONE}); // (IDLE => BUSY [*2:4] => DONE)
cp_st.also('{IDLE, BUSY, BUSY, BUSY, DONE}); cp_st.also('{IDLE, BUSY, BUSY, BUSY, BUSY, DONE});
cp_st.add_goto("flaky", ERR, 2, IDLE); // (ERR [->2] => IDLE)
begin
st_e s1[6] = '{IDLE, BUSY, BUSY, BUSY, DONE, IDLE}; // the lesson's first stream
st_e s2[5] = '{ERR, IDLE, BUSY, ERR, IDLE}; // and its second
foreach (s1[i]) begin cp_arc.sample(s1[i]); cp_st.sample(s1[i]); end
foreach (s2[i]) begin cp_arc.sample(s2[i]); cp_st.sample(s2[i]); end
end
cp_arc.report(); cp_st.report();
if (cp_st.bin_list[0].hits != 1 || cp_st.bin_list[1].hits != 1 || cp_st.bin_list[2].hits != 1) $fatal(1, "FAIL: repetition bins");
if (cp_arc.bin_list[4].hits != 0 || cp_arc.bin_list[5].hits != 2) $fatal(1, "FAIL: arc bins");
$display("PASS: busy3, busy_short and flaky each hit once; happy_loop never did, because BUSY always ran more than once before DONE");
$finish;
end
endmodule coverpoint cp_arc: 83.33% (5 of 6 bins) below goal 100
idle_busy 2
busy_done 1
done_idle 1
any_err 2
happy_loop 0 <- hole
recover 2
coverpoint cp_st: 100.00% (3 of 3 bins)
busy3 1
busy_short 1
flaky 1
PASS: busy3, busy_short and flaky each hit once; happy_loop never did, because BUSY always ran more than once before DONESVA cover. Three signals with elastic timing sit outside single-variable consecutive sampling.
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.
Over the lesson's two streams busy3, busy_short and flaky each hit once, and happy_loop never did, because BUSY always ran more than once before DONE.