Add `--coverage-per-instance`

This commit is contained in:
Yogish Sekhar 2026-05-24 22:08:55 +00:00 committed by GitHub
parent 6b48b772d3
commit cf8713aebc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 417 additions and 0 deletions

View File

@ -377,6 +377,7 @@ detailed descriptions of these arguments.
--coverage-fsm Enable FSM state/arc coverage
--coverage-line Enable line coverage
--coverage-max-width <width> Maximum array depth for coverage
--coverage-per-instance Enable per-instance coverage counters
--coverage-toggle Enable toggle coverage
--coverage-underscore Enable coverage of _signals
--coverage-user Enable SVL user coverage

View File

@ -317,6 +317,20 @@ Summary:
toggle coverage. Defaults to 256, as covering large vectors may greatly
slow coverage simulations.
.. option:: --coverage-per-instance
For Verilator-inserted coverage, preserve generated coverage counters and
``.dat`` records per hierarchy instance. This option must be specified when
Verilating the model, together with one or more coverage instrumentation
options, for example :vlopt:`--coverage`, :vlopt:`--coverage-line`,
:vlopt:`--coverage-toggle`, :vlopt:`--coverage-expr`,
:vlopt:`--coverage-fsm`, or :vlopt:`--coverage-user`. It may increase
generated model size, counter memory, coverage write time, and ``.dat`` file
size.
This does not affect SystemVerilog ``cover``, which uses the IEEE-specified
coverage option ``per_instance``.
.. option:: --coverage-toggle
Enables adding signal toggle coverage. See :ref:`Toggle Coverage`.

View File

@ -157,6 +157,9 @@ private:
if (v3Global.opt.coverage()) {
puts("// Write coverage data (since Verilated with --coverage)\n");
if (v3Global.opt.coveragePerInstance()) {
puts("contextp->coveragep()->forcePerInstance(true);\n");
}
puts("contextp->coveragep()->write();\n");
puts("\n");
}

View File

@ -1360,6 +1360,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-coverage-fsm", OnOff, &m_coverageFsm);
DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth);
DECL_OPTION("-coverage-per-instance", OnOff, &m_coveragePerInstance);
DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle);
DECL_OPTION("-coverage-underscore", OnOff, &m_coverageUnderscore);
DECL_OPTION("-coverage-user", OnOff, &m_coverageUser);

View File

@ -228,6 +228,7 @@ private:
bool m_coverageExpr = false; // main switch: --coverage-expr
bool m_coverageFsm = false; // main switch: --coverage-fsm
bool m_coverageLine = false; // main switch: --coverage-block
bool m_coveragePerInstance = false; // main switch: --coverage-per-instance
bool m_coverageToggle = false; // main switch: --coverage-toggle
bool m_coverageUnderscore = false; // main switch: --coverage-underscore
bool m_coverageUser = false; // main switch: --coverage-func
@ -524,6 +525,7 @@ public:
bool coverageExpr() const { return m_coverageExpr; }
bool coverageFsm() const { return m_coverageFsm; }
bool coverageLine() const { return m_coverageLine; }
bool coveragePerInstance() const { return m_coveragePerInstance; }
bool coverageToggle() const { return m_coverageToggle; }
bool coverageUnderscore() const { return m_coverageUnderscore; }
bool coverageUser() const { return m_coverageUser; }

View File

@ -0,0 +1,90 @@
// // verilator_coverage annotation
// DESCRIPTION: Verilator: Coverage per-instance hierarchy for duplicate module instances
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module child (
input clk,
input en
);
`ifdef INLINE_CHILD //verilator inline_module
`else //verilator no_inline_module
`endif
%000001 reg [3:0] count = 0;
-000001 point: type=line comment=block hier=tb.dut.u_a
-000001 point: type=line comment=block hier=tb.dut.u_b
%000009 always @(posedge clk) begin
-000009 point: type=line comment=block hier=tb.dut.u_a
-000009 point: type=line comment=block hier=tb.dut.u_b
%000008 if (en) begin
-000004 point: type=branch comment=if hier=tb.dut.u_a
-000001 point: type=branch comment=if hier=tb.dut.u_b
-000005 point: type=branch comment=else hier=tb.dut.u_a
-000008 point: type=branch comment=else hier=tb.dut.u_b
%000004 count <= count + 1'b1;
-000004 point: type=branch comment=if hier=tb.dut.u_a
-000001 point: type=branch comment=if hier=tb.dut.u_b
%000008 end else begin
-000005 point: type=branch comment=else hier=tb.dut.u_a
-000008 point: type=branch comment=else hier=tb.dut.u_b
%000008 count <= count;
-000005 point: type=branch comment=else hier=tb.dut.u_a
-000008 point: type=branch comment=else hier=tb.dut.u_b
end
end
endmodule
module t (
input clk
);
%000001 reg [3:0] cyc = 0;
-000001 point: type=line comment=block hier=tb.dut
// Over 9 clock edges, u_a.en is true for cyc 0..3, so u_a should report
// if coverage of 4, else coverage of 5, and line coverage of 9.
child u_a (
.clk(clk),
.en(cyc < 4)
);
// u_b.en is true only for cyc 0, so u_b should report if coverage of 1,
// else coverage of 8, and line coverage of 9.
child u_b (
.clk(clk),
.en(cyc == 0)
);
%000009 always @(posedge clk) begin
-000009 point: type=line comment=block hier=tb.dut
%000009 cyc <= cyc + 1'b1;
-000009 point: type=line comment=block hier=tb.dut
end
endmodule
module tb;
%000001 reg clk = 0;
-000001 point: type=line comment=block hier=tb
t dut (
.clk(clk)
);
000017 always #1 clk = !clk;
+000017 point: type=line comment=block hier=tb
%000009 always @(posedge clk) begin
-000009 point: type=line comment=block hier=tb
%000008 if (dut.cyc == 8) begin
-000001 point: type=branch comment=if hier=tb
-000008 point: type=branch comment=else hier=tb
%000001 $write("*-* All Finished *-*\n");
-000001 point: type=branch comment=if hier=tb
%000001 $finish;
-000001 point: type=branch comment=if hier=tb
end
end
endmodule

View File

@ -0,0 +1,64 @@
// DESCRIPTION: Verilator: Coverage per-instance hierarchy for duplicate module instances
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module child (
input clk,
input en
);
`ifdef INLINE_CHILD //verilator inline_module
`else //verilator no_inline_module
`endif
reg [3:0] count = 0;
always @(posedge clk) begin
if (en) begin
count <= count + 1'b1;
end else begin
count <= count;
end
end
endmodule
module t (
input clk
);
reg [3:0] cyc = 0;
// Over 9 clock edges, u_a.en is true for cyc 0..3, so u_a should report
// if coverage of 4, else coverage of 5, and line coverage of 9.
child u_a (
.clk(clk),
.en(cyc < 4)
);
// u_b.en is true only for cyc 0, so u_b should report if coverage of 1,
// else coverage of 8, and line coverage of 9.
child u_b (
.clk(clk),
.en(cyc == 0)
);
always @(posedge clk) begin
cyc <= cyc + 1'b1;
end
endmodule
module tb;
reg clk = 0;
t dut (
.clk(clk)
);
always #1 clk = !clk;
always @(posedge clk) begin
if (dut.cyc == 8) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3,
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import os
import vltest_bootstrap
test.scenarios('simulator')
test.compile(top_filename="t/t_cover_per_instance.v",
v_flags2=["+define+INLINE_CHILD"],
verilator_flags2=[
'--binary',
'--coverage-line',
'--coverage-per-instance',
'--top-module',
'tb',
'--timing',
])
test.execute(all_run_flags=[" +verilator+coverage+file+" + test.obj_dir + "/coverage.dat"])
cov = test.obj_dir + "/coverage.dat"
test.run(cmd=[
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
"--annotate-points",
"--annotate",
test.obj_dir + "/annotated-points",
cov,
],
verilator_run=True)
test.files_identical(test.obj_dir + "/annotated-points/t_cover_per_instance.v",
"t/t_cover_per_instance.out")
test.passes()

View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3,
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import os
import vltest_bootstrap
test.scenarios('simulator')
test.compile(top_filename="t/t_cover_per_instance.v",
verilator_flags2=[
'--binary',
'--coverage-line',
'--coverage-per-instance',
'--top-module',
'tb',
'--timing',
])
test.execute(all_run_flags=[" +verilator+coverage+file+" + test.obj_dir + "/coverage.dat"])
cov = test.obj_dir + "/coverage.dat"
test.run(cmd=[
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
"--annotate-points",
"--annotate",
test.obj_dir + "/annotated-points",
cov,
],
verilator_run=True)
test.files_identical(test.obj_dir + "/annotated-points/t_cover_per_instance.v",
"t/t_cover_per_instance.out")
test.passes()

View File

@ -0,0 +1,62 @@
# SystemC::Coverage-3
C 'ft/t_cover_per_instance_user.vl11n18tlinepagev_line/childoblockS11htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl11n18tlinepagev_line/childoblockS11htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl11n7ttogglepagev_toggle/childoobserved:0->1htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl11n7ttogglepagev_toggle/childoobserved:0->1htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl11n7ttogglepagev_toggle/childoobserved:1->0htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl11n7ttogglepagev_toggle/childoobserved:1->0htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[0]:0->1htb.dut_a' 2
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[0]:0->1htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[0]:1->0htb.dut_a' 2
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[0]:1->0htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[1]:0->1htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[1]:0->1htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[1]:1->0htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[1]:1->0htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[2]:0->1htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[2]:0->1htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[2]:1->0htb.dut_a' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[2]:1->0htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[3]:0->1htb.dut_a' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[3]:0->1htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[3]:1->0htb.dut_a' 0
C 'ft/t_cover_per_instance_user.vl12n13ttogglepagev_toggle/childocount[3]:1->0htb.wrap_b.dut_b' 0
C 'ft/t_cover_per_instance_user.vl12n21tlinepagev_line/childoblockS12htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl12n21tlinepagev_line/childoblockS12htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl15n5tuserpagev_user/childosame_stmtS15htb.dut_a.same_stmt' 4
C 'ft/t_cover_per_instance_user.vl15n5tuserpagev_user/childosame_stmtS15htb.wrap_b.dut_b.same_stmt' 1
C 'ft/t_cover_per_instance_user.vl17n3tlinepagev_line/childoblockS17-18htb.dut_a' 9
C 'ft/t_cover_per_instance_user.vl17n3tlinepagev_line/childoblockS17-18htb.wrap_b.dut_b' 9
C 'ft/t_cover_per_instance_user.vl19n5tbranchpagev_branch/childoifS19-20htb.dut_a' 4
C 'ft/t_cover_per_instance_user.vl19n5tbranchpagev_branch/childoifS19-20htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl19n6tbranchpagev_branch/childoelseS21-22htb.dut_a' 5
C 'ft/t_cover_per_instance_user.vl19n6tbranchpagev_branch/childoelseS21-22htb.wrap_b.dut_b' 8
C 'ft/t_cover_per_instance_user.vl28n11ttogglepagev_toggle/wrapoclk:0->1htb.wrap_b' 9
C 'ft/t_cover_per_instance_user.vl28n11ttogglepagev_toggle/wrapoclk:1->0htb.wrap_b' 8
C 'ft/t_cover_per_instance_user.vl29n11ttogglepagev_toggle/wrapoen:0->1htb.wrap_b' 1
C 'ft/t_cover_per_instance_user.vl29n11ttogglepagev_toggle/wrapoen:1->0htb.wrap_b' 1
C 'ft/t_cover_per_instance_user.vl38n13tlinepagev_line/tboblockS38htb' 1
C 'ft/t_cover_per_instance_user.vl38n7ttogglepagev_toggle/tboclk:0->1htb' 9
C 'ft/t_cover_per_instance_user.vl38n7ttogglepagev_toggle/tboclk:1->0htb' 8
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[0]:0->1htb' 5
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[0]:1->0htb' 4
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[1]:0->1htb' 2
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[1]:1->0htb' 2
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[2]:0->1htb' 1
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[2]:1->0htb' 1
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[3]:0->1htb' 1
C 'ft/t_cover_per_instance_user.vl39n13ttogglepagev_toggle/tbocyc[3]:1->0htb' 0
C 'ft/t_cover_per_instance_user.vl39n19tlinepagev_line/tboblockS39htb' 1
C 'ft/t_cover_per_instance_user.vl53n3tlinepagev_line/tboblockS53-54htb' 9
C 'ft/t_cover_per_instance_user.vl57n3tlinepagev_line/tboblockS57htb' 17
C 'ft/t_cover_per_instance_user.vl59n3tlinepagev_line/tboblockS59htb' 9
C 'ft/t_cover_per_instance_user.vl60n5tbranchpagev_branch/tboifS60-62htb' 1
C 'ft/t_cover_per_instance_user.vl60n6tbranchpagev_branch/tboelsehtb' 8
C 'ft/t_cover_per_instance_user.vl8n11ttogglepagev_toggle/childoclk:0->1htb.dut_a' 9
C 'ft/t_cover_per_instance_user.vl8n11ttogglepagev_toggle/childoclk:0->1htb.wrap_b.dut_b' 9
C 'ft/t_cover_per_instance_user.vl8n11ttogglepagev_toggle/childoclk:1->0htb.dut_a' 8
C 'ft/t_cover_per_instance_user.vl8n11ttogglepagev_toggle/childoclk:1->0htb.wrap_b.dut_b' 8
C 'ft/t_cover_per_instance_user.vl9n11ttogglepagev_toggle/childoen:0->1htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl9n11ttogglepagev_toggle/childoen:0->1htb.wrap_b.dut_b' 1
C 'ft/t_cover_per_instance_user.vl9n11ttogglepagev_toggle/childoen:1->0htb.dut_a' 1
C 'ft/t_cover_per_instance_user.vl9n11ttogglepagev_toggle/childoen:1->0htb.wrap_b.dut_b' 1

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3,
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=[
'--binary',
'--assert',
'--coverage-line',
'--coverage-toggle',
'--coverage-user',
'--coverage-per-instance',
'--top-module',
'tb',
'--timing',
])
test.execute(all_run_flags=[" +verilator+coverage+file+" + test.obj_dir + "/coverage.dat"])
test.files_identical(test.obj_dir + "/coverage.dat", "t/" + test.name + ".out")
test.passes()

View File

@ -0,0 +1,65 @@
// DESCRIPTION: Verilator: User coverage per-instance records for the same statement
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module child (
input clk,
input en
);
reg observed = 1'b0;
reg [3:0] count = 0;
same_stmt:
cover property (@(posedge clk) en);
always @(posedge clk) begin
observed <= en;
if (en) begin
count <= count + 1'b1;
end else begin
count <= count;
end
end
endmodule
module wrap (
input clk,
input en
);
child dut_b (
.clk(clk),
.en(en)
);
endmodule
module tb;
reg clk = 0;
reg [3:0] cyc = 0;
// Same user cover statement at two different hierarchy points.
// Expected with per_instance: dut_a count=4, wrap_b.dut_b count=1.
child dut_a (
.clk(clk),
.en(cyc < 4)
);
wrap wrap_b (
.clk(clk),
.en(cyc == 0)
);
always @(posedge clk) begin
cyc <= cyc + 1'b1;
end
always #1 clk = !clk;
always @(posedge clk) begin
if (cyc == 8) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule