Covergroups are not write-only. Methods trigger sampling procedurally, read the current percentage, and pause collection, which turns coverage into a runtime signal the test can react to.
Call | What it does |
|---|---|
| Triggers a sampling event procedurally |
| Samples explicitly passed values instead of a clock event |
| This instance percentage |
| Type-level percentage over all instances (average by default, union with |
| Pause and resume collection on one instance |
| Sets the instance name procedurally when names are computed |
| Overall functional coverage across all covergroups |
The following listing is not run in this course's checks: the open simulators do not count covergroups.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
covergroup txn_cg with function sample(bit wr, logic [3:0] len);
option.per_instance = 1;
cp_wr : coverpoint wr { bins rd = {0}; bins wr_ = {1}; }
cp_len : coverpoint len { bins s = {1}; bins m = {[2:8]}; bins l = {[9:15]}; }
x_wl : cross cp_wr, cp_len;
endgroup
txn_cg cg = new();
task collect(bit wr, logic [3:0] len);
cg.sample(wr, len); // explicit, argument-passing sample
$display("inst=%0.1f type=%0.1f",
cg.get_inst_coverage(), cg.get_coverage());
endtaskSampling both on a clock event and procedurally double counts. Pick one sampling style per covergroup.
The highest-value use of the query API is a test that watches its own coverage. The classic pattern caps random stimulus by achievement instead of by a fixed transaction count.
The following listing is not run in this course's checks: the open simulators do not count covergroups.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
// Run until the coverage goal or the transaction budget, whichever first
task run_random_phase(int max_txns = 10000);
int n = 0;
while (n < max_txns) begin
drive_random_txn(); // stimulus with cg.sample inside
n++;
if ((n % 100) == 0) begin // poll every 100 txns, not every txn
if (cg.get_inst_coverage() >= cg.option.goal) begin
$display("goal hit after %0d txns, stopping random phase", n);
break;
end
end
end
if (cg.get_inst_coverage() < cg.option.goal)
$display("budget spent below goal, holes need directed tests");
endtaskPoll on a decimated schedule. Each query walks bin state, so per-transaction polling slows hot paths.
set_inst_name does procedurally what option.name does declaratively.
$get_coverage is coarse but handy for a single end-of-test gate.
Per-coverpoint queries let adaptive tests steer at finer grain than the whole group.
Reset and error-recovery traffic is not plan traffic. Gating keeps closure honest by excluding those windows from collection.
The following listing is not run in this course's checks: the open simulators do not count covergroups.
// not-run: Verilator 5.048 ignores covergroups (COVERIGN) and Icarus 13 has none; the bins model counts it
bit collecting = 0;
initial begin
cg.stop(); // nothing counts yet
wait (rst_n == 1'b1);
repeat (10) @(posedge clk); // settle cycles
cg.start(); // begin honest collection
collecting = 1'b1;
end
// Synchronous recovery gate: suspect traffic never counts
always @(posedge clk) begin
if (fatal && collecting) begin
cg.stop();
collecting = 1'b0;
end
else if (recovered && !collecting) begin
cg.start();
collecting = 1'b1;
end
endSamples during a stopped window are simply not counted. A stop without a matching start silently collects nothing for the rest of the test.
get_coverage and get_inst_coverage return different numbers?When several instances exist. One reads the type-level view and the other reads a single instance.
No. That is one seed view. Sign-off closure reads the merged regression database, which the coverage-closure topic owns.
The bench builds txn_cg with the bins model and a sample(wr, len) function. It stops collection over 20 reset-time samples, starts it, then runs random traffic that polls the coverage every 100 transactions and stops at the goal.
0%: a stopped instance counts nothing. The random phase can only stop on a poll, so at a multiple of 100 transactions; on this stream it stops after 500, once the rare write and single-beat cells are all hit.
// uses: covmodel
// sample() with arguments, stop() and start() around reset, and a random phase that stops on its goal.
module tb_methods;
cm_group txn = new("txn_cg");
cm_point cp_wr = new("cp_wr"), cp_len = new("cp_len");
cm_cross x_wl;
int unsigned r = 32'd7;
function int unsigned rnd(int unsigned n); // a fixed generator, so every simulator sees the same stream
r = r * 32'd1103515245 + 32'd12345;
return (r >> 8) % n;
endfunction
// covergroup txn_cg with function sample(bit wr, logic [3:0] len);
function void sample(bit wr, int len);
cp_wr.sample(wr); cp_len.sample(len); x_wl.sample();
endfunction
function void stop(); cp_wr.enabled = 0; cp_len.enabled = 0; endfunction
function void start(); cp_wr.enabled = 1; cp_len.enabled = 1; endfunction
initial begin
int n = 0;
cp_wr.add("rd", 0, 0); cp_wr.add("wr_", 1, 1);
cp_len.add("s", 1, 1); cp_len.add("m", 2, 8); cp_len.add("l", 9, 15);
x_wl = new("x_wl", cp_wr, cp_len);
txn.points.push_back(cp_wr); txn.points.push_back(cp_len); txn.crosses.push_back(x_wl);
// Reset traffic: stop() before it, start() after the settle cycles.
stop();
repeat (20) sample(rnd(2) == 1, 1 + rnd(15));
$display(" after 20 samples while stopped: %0.2f%%", txn.get_coverage());
if (txn.get_coverage() != 0.0) $fatal(1, "FAIL: a stopped covergroup counted");
start();
// Run until the goal or the budget, whichever comes first, polling every 100 transactions.
while (n < 10000) begin
sample(rnd(4) == 0, rnd(8) == 0 ? 1 + rnd(15) : 2 + rnd(7)); // writes and single beats are rare
n++;
if (n % 100 == 0 && txn.get_coverage() >= txn.goal) break;
end
$display(" goal reached after %0d transactions; one poll per 100", n);
txn.report();
if (txn.get_coverage() != 100.0 || n % 100 != 0 || n >= 10000) $fatal(1, "FAIL: the adaptive loop");
$display("PASS: the stopped window counted nothing, and the random phase stopped itself at the goal");
$finish;
end
endmodule after 20 samples while stopped: 0.00%
goal reached after 500 transactions; one poll per 100
covergroup txn_cg: 100.00% (goal 100)
coverpoint cp_wr: 100.00% (2 of 2 bins)
rd 378
wr_ 122
coverpoint cp_len: 100.00% (3 of 3 bins)
s 5
m 465
l 30
cross x_wl: 100.00% (6 of 6 bins)
PASS: the stopped window counted nothing, and the random phase stopped itself at the goalCounted 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.
20 samples while stopped counted nothing, and the random phase stopped itself at the goal after 500 transactions, on a poll.