Every covergroup type has one type-level number for all its instances. type_option.merge_instances picks how it is formed. The default, 0, takes the weighted average of the instances' coverage. With 1, the instances' bins are merged as a union, as if one instance had seen every sample.
Setting option.per_instance to 1 also keeps each instance's own hit counts and gives it its own report row, alongside the type-level number. Set it in the covergroup body; it cannot be changed procedurally.
Instance | Type-level only | Per-instance ( |
|---|---|---|
mon[0].cg | One type number for all four: | port0_cov: 97% |
mon[1].cg | union ( | port1_cov: 95% |
mon[2].cg | average ( | port2_cov: 96% |
mon[3].cg | Port 3 broken or idle: invisible | port3_cov: 3%, one glance away |
The table is the whole argument. The union reads 98% because the healthy ports hit the bins the dead one missed. The default average reads about 73%, which looks like a stimulus gap, not a dead port. Either way the type number cannot say which port is broken. Per-instance coverage exists to make per-unit health visible.
With per-instance mode on, the report must tell instances apart. 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 port_cg (string inst_name) @(posedge clk iff valid);
option.per_instance = 1;
option.name = inst_name; // report row label
option.comment = "vplan 3.2 per-port traffic shapes";
cp_kind : coverpoint pkt_kind { bins data = {0}; bins ctrl = {1}; }
cp_size : coverpoint pkt_size { bins s = {[1:64]}; bins m = {[65:512]};
bins l = {[513:1500]}; }
x_ks : cross cp_kind, cp_size;
endgroup
// One definition, four named instances:
port_cg cov0 = new("port0_cov");
port_cg cov1 = new("port1_cov");
port_cg cov2 = new("port2_cov");
port_cg cov3 = new("port3_cov");option.name labels the row and option.comment carries plan traceability. Pass the name through a constructor argument so each instance differs.
option.per_instance = 1 keeps per-instance counts and rows. Its default is 0: only the type-level number is reported.
type_option.merge_instances sets how the type-level number is formed: 0 (default) averages the instances, 1 takes the union of their bins.
option.name replaces tool-generated hierarchy strings with a readable label.
option.comment holds free text, typically the plan section the group covers.
option.weight and option.goal are also settable per instance. The next lesson covers them.
Situation | Accumulation to use |
|---|---|
Per-port or per-channel monitors | Per-instance. Each unit must prove its own traffic; symmetry bugs hide in the merge. |
One covergroup instantiated exactly once | Either. Type and instance views coincide, though a name still tidies the report. |
Hundreds of instances, one per flow | Type-level or coarser grouping. Per-instance rows multiply database size and report noise. |
Channels provably symmetric with round-robin stimulus | Type-level is defensible. Per-instance still helps during debug. |
Both views are readable at runtime. 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
port_cg cov0 = new("port0_cov");
port_cg cov1 = new("port1_cov");
port_cg cov2 = new("port2_cov");
port_cg cov3 = new("port3_cov");
initial begin
// End-of-test per-port health check, inside procedural code:
if (cov3.get_inst_coverage() < 50.0)
$display("WARNING: port3 coverage low, dead port?");
endget_coverage reads the type-level number and get_inst_coverage reads one instance. They answer different questions and diverge exactly when per-instance mode matters.
get_coverage and get_inst_coverage?get_coverage returns the type-level number across all instances: the average by default, or the union with merge_instances = 1. get_inst_coverage returns one instance only. With a single instance the two coincide.
The bench builds four instances of port_cg with the bins model. Ports 0 to 2 each miss a different kind-and-size pair and port 3 sees nothing. A fifth instance sees every sample, which is what merge_instances = 1 computes.
Each live port reads 94.44%: both coverpoints full, the cross 5 of 6. Port 3 reads 0%. The average is 70.83%, and the union is 100%, because each pair one port missed, another port hit.
// uses: covmodel
// Four instances of port_cg: the type-level average, the union of their bins, and the per-instance rows.
class port_cov; // covergroup port_cg (string inst_name), with per_instance = 1
cm_group cg;
cm_point cp_kind, cp_size;
cm_cross x_ks;
function new(string inst_name);
cg = new(inst_name); // option.name = inst_name
cp_kind = new("cp_kind"); cp_size = new("cp_size");
cp_kind.add("data", 0, 0); cp_kind.add("ctrl", 1, 1);
cp_size.add("s", 1, 64); cp_size.add("m", 65, 512); cp_size.add("l", 513, 1500);
x_ks = new("x_ks", cp_kind, cp_size);
cg.points.push_back(cp_kind); cg.points.push_back(cp_size); cg.crosses.push_back(x_ks);
endfunction
function void sample(int kind, int size);
cp_kind.sample(kind); cp_size.sample(size); x_ks.sample();
endfunction
endclass
port0_cov: 94.44%
port1_cov: 94.44%
port2_cov: 94.44%
port3_cov: 0.00%
type level, average (merge_instances = 0): 70.83%
type level, union (merge_instances = 1): 100.00%
port3 coverage low: dead port?
PASS: the union read 100% and the average 70.83%; only the per-instance rows showed port3 at 0Counted 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.
Three live ports read 94.44% each and the dead one 0%; the average read 70.83% and the union 100%, and only the rows named port 3.