diff --git a/test_regress/t/t_cover_line_option.info.out b/test_regress/t/t_cover_line_option.info.out new file mode 100644 index 000000000..eaba1df5d --- /dev/null +++ b/test_regress/t/t_cover_line_option.info.out @@ -0,0 +1,87 @@ +TN:verilator_coverage +SF:t/t_cover_line.v +DA:15,1 +DA:18,1 +DA:55,10 +DA:83,1 +DA:84,1 +DA:85,1 +DA:87,1 +DA:88,1 +DA:89,1 +DA:91,7 +BRDA:91,0,0,1 +BRDA:91,0,1,7 +DA:92,1 +DA:93,1 +DA:96,7 +DA:97,7 +DA:100,0 +DA:101,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:107,0 +DA:110,1 +DA:111,1 +DA:113,1 +DA:115,1 +DA:127,1 +DA:129,1 +DA:140,20 +DA:145,18 +DA:164,20 +DA:165,20 +DA:174,18 +DA:188,1 +DA:189,1 +DA:194,11 +DA:199,11 +DA:215,10 +DA:216,10 +DA:219,11 +DA:221,11 +DA:229,11 +DA:230,1 +DA:231,11 +DA:232,11 +DA:252,10 +DA:265,10 +DA:266,10 +DA:267,1 +DA:268,1 +DA:269,1 +DA:270,1 +DA:271,1 +DA:272,5 +DA:276,10 +DA:277,10 +DA:287,0 +DA:294,0 +DA:300,1 +DA:303,1 +DA:304,20 +DA:305,20 +DA:314,1 +DA:317,21 +DA:318,21 +DA:319,21 +DA:322,10 +DA:324,10 +DA:330,10 +DA:331,10 +DA:332,10 +DA:346,11 +DA:350,11 +DA:353,55 +BRDA:353,0,0,11 +BRDA:353,0,1,55 +DA:354,55 +DA:356,44 +BRDA:356,0,0,11 +BRDA:356,0,1,44 +DA:357,44 +BRF:6 +BRH:4 +end_of_record diff --git a/test_regress/t/t_cover_line_option.out b/test_regress/t/t_cover_line_option.out new file mode 100644 index 000000000..f02558ba2 --- /dev/null +++ b/test_regress/t/t_cover_line_option.out @@ -0,0 +1,444 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2008 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg toggle; +%000001 initial toggle=0; +-000001 point: comment=block hier=top.t + + integer cyc; +%000001 initial cyc=1; +-000001 point: comment=block hier=top.t + + wire [7:0] cyc_copy = cyc[7:0]; + + alpha a1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + alpha a2 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + beta b1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + beta b2 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + tsk t1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + off o1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + tab tab1 (/*AUTOINST*/ + // Inputs + .clk (clk)); + par par1 (/*AUTOINST*/); + cond cond1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .cyc (cyc)); + + 000010 always @ (posedge clk) begin ++000010 point: comment=block hier=top.t + if (cyc!=0) begin + cyc <= cyc + 1; + toggle <= '0; + // Single and multiline if + if (cyc==3) $write(""); + if (cyc==3) + begin + $write(""); + end + // Single and multiline else + if (cyc==3) ; else $write(""); + if (cyc==3) ; + else + begin + $write(""); + end + // Single and multiline if else + if (cyc==3) $write(""); else $write(""); + if (cyc==3) + begin + $write(""); + end + else + begin + $write(""); + end + // multiline elseif +%000001 if (cyc==3) +-000001 point: comment=elsif hier=top.t +%000001 begin +-000001 point: comment=elsif hier=top.t +%000001 $write(""); +-000001 point: comment=elsif hier=top.t + end +%000001 else if (cyc==4) +-000001 point: comment=elsif hier=top.t +%000001 begin +-000001 point: comment=elsif hier=top.t +%000001 $write(""); +-000001 point: comment=elsif hier=top.t + end +%000007 else if (cyc==5) +-000001 point: comment=if hier=top.t +-000007 point: comment=else hier=top.t +%000001 begin +-000001 point: comment=if hier=top.t +%000001 $write(""); +-000001 point: comment=if hier=top.t + end + else +%000007 begin +-000007 point: comment=else hier=top.t +%000007 $write(""); +-000007 point: comment=else hier=top.t + end + // Single and multiline while +%000000 while (0); +-000000 point: comment=block hier=top.t +%000000 while (0) begin +-000000 point: comment=block hier=top.t +%000000 $write(""); +-000000 point: comment=block hier=top.t + end +%000000 do ; while (0); +-000000 point: comment=block hier=top.t +%000000 do begin +-000000 point: comment=block hier=top.t +%000000 $write(""); +-000000 point: comment=block hier=top.t +%000000 end while (0); +-000000 point: comment=block hier=top.t + //=== + // Task and complicated +%000001 if (cyc==3) begin +-000001 point: comment=elsif hier=top.t +%000001 toggle <= '1; +-000001 point: comment=elsif hier=top.t + end +%000001 else if (cyc==5) begin +-000001 point: comment=elsif hier=top.t + `ifdef VERILATOR +%000001 $c("this->call_task();"); +-000001 point: comment=elsif hier=top.t + `else + call_task(); + `endif + end + else if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +%000001 task call_task; +-000001 point: comment=block hier=top.t + /* verilator public */ +%000001 t1.center_task(1'b1); +-000001 point: comment=block hier=top.t + endtask + + endmodule + + module alpha (/*AUTOARG*/ + // Inputs + clk, toggle + ); + input clk; + input toggle; + 000020 always @ (posedge clk) begin ++000020 point: comment=block hier=top.t.a* + if (toggle) begin // CHECK_COVER(0,"top.t.a*",18) + $write(""); + // t.a1 and t.a2 collapse to a count of 2 + end + 000018 if (toggle) begin // *** t_cover_line.vlt turns this off ++000018 point: comment=else hier=top.t.a* + $write(""); // CHECK_COVER_MISSING(0) + // This doesn't even get added + `ifdef ATTRIBUTE + // verilator coverage_block_off + `endif + end + end + endmodule + + module beta (/*AUTOARG*/ + // Inputs + clk, toggle + ); + input clk; + input toggle; + + /* verilator public_module */ + + 000020 always @ (posedge clk) begin ++000020 point: comment=block hier=top.t.b* + 000020 $write(""); // Always covered ++000020 point: comment=block hier=top.t.b* + if (0) begin // CHECK_COVER(0,"top.t.b*",0) + // Make sure that we don't optimize away zero buckets + $write(""); + end + if (toggle) begin // CHECK_COVER(0,"top.t.b*",2) + // t.b1 and t.b2 collapse to a count of 2 + $write(""); + end + 000018 if (toggle) begin : block ++000018 point: comment=else hier=top.t.b* + // This doesn't + `ifdef ATTRIBUTE + // verilator coverage_block_off + `endif + begin end // *** t_cover_line.vlt turns this off (so need begin/end) + if (1) begin end // CHECK_COVER_MISSING(0) + $write(""); // CHECK_COVER_MISSING(0) + end + end + endmodule + + class Cls; + bit m_toggle; +%000001 function new(bit toggle); +-000001 point: comment=block hier=top.$unit::Cls__Vclpkg +%000001 m_toggle = toggle; +-000001 point: comment=block hier=top.$unit::Cls__Vclpkg + if (m_toggle) begin // CHECK_COVER(0,"top.$unit::Cls",1) + $write(""); + end + endfunction + 000011 static function void fstatic(bit toggle); ++000011 point: comment=block hier=top.$unit::Cls__Vclpkg + if (1) begin // CHECK_COVER(0,"top.$unit::Cls",1) + $write(""); + end + endfunction + 000011 function void fauto(); ++000011 point: comment=block hier=top.$unit::Cls__Vclpkg + if (m_toggle) begin // CHECK_COVER(0,"top.$unit::Cls",11) + $write(""); + end + endfunction + endclass + + module tsk (/*AUTOARG*/ + // Inputs + clk, toggle + ); + input clk; + input toggle; + + /* verilator public_module */ + + 000010 always @ (posedge clk) begin ++000010 point: comment=block hier=top.t.t1 + 000010 center_task(1'b0); ++000010 point: comment=block hier=top.t.t1 + end + + 000011 task center_task; ++000011 point: comment=block hier=top.t.t1 + input external; + 000011 begin ++000011 point: comment=block hier=top.t.t1 + if (toggle) begin // CHECK_COVER(0,"top.t.t1",1) + $write(""); + end + if (external) begin // CHECK_COVER(0,"top.t.t1",1) + $write("[%0t] Got external pulse\n", $time); + end + end + 000011 begin ++000011 point: comment=block hier=top.t.t1 +%000001 Cls c = new(1'b1); +-000001 point: comment=block hier=top.t.t1 + 000011 c.fauto(); ++000011 point: comment=block hier=top.t.t1 + 000011 Cls::fstatic(1'b1); ++000011 point: comment=block hier=top.t.t1 + end + endtask + endmodule + + module off (/*AUTOARG*/ + // Inputs + clk, toggle + ); + input clk; + input toggle; + + // verilator coverage_off + always @ (posedge clk) begin + if (toggle) begin + $write(""); // CHECK_COVER_MISSING(0) + // because under coverage_module_off + end + end + // verilator coverage_on + 000010 always @ (posedge clk) begin ++000010 point: comment=block hier=top.t.o1 + if (toggle) begin + // because under coverage_module_off + $write(""); + if (0) ; // CHECK_COVER(0,"top.t.o1",1) + end + end + endmodule + + module tab (input clk); + bit [3:0] cyc4; + int decoded; + + 000010 always @ (posedge clk) begin ++000010 point: comment=block hier=top.t.tab1 + 000010 case (cyc4) ++000010 point: comment=block hier=top.t.tab1 +%000001 1: decoded = 10; +-000001 point: comment=case hier=top.t.tab1 +%000001 2: decoded = 20; +-000001 point: comment=case hier=top.t.tab1 +%000001 3: decoded = 30; +-000001 point: comment=case hier=top.t.tab1 +%000001 4: decoded = 40; +-000001 point: comment=case hier=top.t.tab1 +%000001 5: decoded = 50; +-000001 point: comment=case hier=top.t.tab1 +%000005 default: decoded = 0; +-000005 point: comment=case hier=top.t.tab1 + endcase + end + + 000010 always @ (posedge clk) begin ++000010 point: comment=block hier=top.t.tab1 + 000010 cyc4 <= cyc4 + 1; ++000010 point: comment=block hier=top.t.tab1 + end + endmodule + + module par(); + localparam int CALLS_FUNC = param_func(1); + + // We don't currently count elaboration time use towards coverage. This + // seems safer for functions used both at elaboration time and not - but may + // revisit this. +%000000 function automatic int param_func(int i); +-000000 point: comment=block hier=top.t.par1 + if (i == 0) begin + i = 99; // Uncovered + end + else begin + i = i + 1; + end +%000000 return i; +-000000 point: comment=block hier=top.t.par1 + endfunction + + endmodule + + package my_pkg; +%000001 int x = 1 ? 1 : 0; +-000001 point: comment=block hier=top.my_pkg + endpackage + +%000001 class Getter1; +-000001 point: comment=block hier=top.$unit::Getter1__Vclpkg + 000020 function int get_1; ++000020 point: comment=block hier=top.$unit::Getter1__Vclpkg + 000020 return 1; ++000020 point: comment=block hier=top.$unit::Getter1__Vclpkg + endfunction + endclass + + module cond(input logic clk, input int cyc); + logic a, b, c, d, e, f, g, h, k, l, m; + logic [5:0] tab; + typedef logic [7:0] arr_t[1:0]; + arr_t data[1:0]; +%000001 Getter1 getter1 = new; +-000001 point: comment=block hier=top.t.cond1 + string s; + + 000021 function logic func_side_effect; ++000021 point: comment=block hier=top.t.cond1 + 000021 $display("SIDE EFFECT"); ++000021 point: comment=block hier=top.t.cond1 + 000021 return 1; ++000021 point: comment=block hier=top.t.cond1 + endfunction + + 000010 function arr_t get_arr; ++000010 point: comment=block hier=top.t.cond1 + arr_t arr; + 000010 return arr; ++000010 point: comment=block hier=top.t.cond1 + endfunction + + assign a = (cyc == 0) ? clk : 1'bz; + assign b = (cyc == 1) ? clk : 0; + assign c = func_side_effect() ? clk : 0; + 000010 always @(posedge clk) begin ++000010 point: comment=block hier=top.t.cond1 + 000010 d = (cyc % 3 == 0) ? 1 : 0; ++000010 point: comment=block hier=top.t.cond1 + 000010 s = (getter1.get_1() == 0) ? "abcd" : $sformatf("%d", getter1.get_1()[4:0]); ++000010 point: comment=block hier=top.t.cond1 + end + assign e = (cyc % 3 == 1) ? (clk ? 1 : 0) : 1; + + // ternary operator in condition shouldn't be included to the coverae + assign f = (cyc != 0 ? 1 : 0) ? 1 : 0; + // the same as in index + assign tab[clk ? 1 : 0] = 1; + assign m = tab[clk ? 3 : 4]; + + for (genvar i = 0; i < 2; i++) begin + assign g = clk ? 1 : 0; + end + + 000011 always begin ++000011 point: comment=block hier=top.t.cond1 + if (cyc == 5) h = cyc > 5 ? 1 : 0; + else h = 1; + + 000011 data[0] = (cyc == 2) ? '{8'h01, 8'h02} : get_arr(); ++000011 point: comment=block hier=top.t.cond1 + + // ternary operator in conditions should be skipped + 000055 for (int i = 0; (i < 5) ? 1 : 0; i++) begin ++000011 point: comment=block hier=top.t.cond1 ++000055 point: comment=block hier=top.t.cond1 + 000055 k = 1'(i); ++000055 point: comment=block hier=top.t.cond1 + end + 000044 for (int i = 0; i < 7; i = (i > 4) ? i + 1 : i + 2) begin ++000011 point: comment=block hier=top.t.cond1 ++000044 point: comment=block hier=top.t.cond1 + 000044 k = 1'(i); ++000044 point: comment=block hier=top.t.cond1 + end + + if (k ? 1 : 0) k = 1; + else k = 0; + end + endmodule + diff --git a/test_regress/t/t_cover_line_option.py b/test_regress/t/t_cover_line_option.py new file mode 100755 index 000000000..f729b2539 --- /dev/null +++ b/test_regress/t/t_cover_line_option.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') +test.top_filename = "t/t_cover_line.v" +test.golden_filename = "t/t_cover_line_option.out" + +test.compile(verilator_flags2=['--cc --coverage +define+ATTRIBUTE']) + +test.execute() + +test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", test.obj_dir + "/annotated", + "--filter-type line", + test.obj_dir + "/coverage.dat"], + verilator_run=True) # yapf:disable + +test.files_identical(test.obj_dir + "/annotated/t_cover_line.v", test.golden_filename) + +# Also try lcov +test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--write-info", test.obj_dir + "/coverage.info", + "--filter-type line", + test.obj_dir + "/coverage.dat"], + verilator_run=True) # yapf:disable + +test.files_identical(test.obj_dir + "/coverage.info", "t/" + test.name + ".info.out") + +# If installed +nout = test.run_capture("lcov --version", check=False) +version_match = re.search(r'version ([0-9.]+)', nout, re.IGNORECASE) +if not version_match: + test.skip("lcov or genhtml not installed") + +if float(version_match.group(1)) < 1.14: + test.skip("lcov or genhtml too old (version " + version_match.group(1) + + ", need version >= 1.14") + +test.run(cmd=[ + "genhtml", test.obj_dir + "/coverage.info", "--branch-coverage", "--output-directory " + + test.obj_dir + "/html" +]) + +test.passes()