diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 43f34081d..66cdfb46d 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1067,7 +1067,7 @@ class AstCoverBin final : public AstNode { public: AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal, - bool isWildcard = false) + bool isWildcard = false, AstNodeExpr* iffp = nullptr) : ASTGEN_SUPER_CoverBin(fl) , m_name{name} , m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL @@ -1076,6 +1076,7 @@ public: : VCoverBinsType::BINS_USER))} , m_isWildcard{isWildcard} { addRangesp(rangesp); + this->iffp(iffp); } // Constructor for automatic bins AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep) diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 12ec751df..0cf93d3fc 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -1017,6 +1017,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { + " hit in coverpoint " + coverpointp->prettyNameQ())); } + if (binp->iffp()) condp = new AstLogAnd{fl, binp->iffp()->cloneTree(false), condp}; AstNodeExpr* const guardedp = applyCoverpointIffCondition(coverpointp, fl, condp); UASSERT_OBJ(m_sampleFuncp, binp, "sample() CFunc not set for coverpoint"); m_sampleFuncp->addStmtsp(new AstIf{fl, guardedp, actionp, nullptr}); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index a1ae67069..163a0f44f 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -352,6 +352,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } puts("}"); } + if (nodep->iffp()) { + puts(" iff ("); + iterateConst(nodep->iffp()); + puts(")"); + } puts(";\n"); } void visit(AstCoverBinsof* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index bfb4750ae..fc29a7f14 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2101,7 +2101,7 @@ class WidthVisitor final : public VNVisitor { } void visit(AstCoverBin* nodep) override { widthCovergroupRanges(nodep->rangesp()); - userIterateAndNext(nodep->iffp(), nullptr); + if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH); userIterateAndNext(nodep->arraySizep(), nullptr); userIterateAndNext(nodep->transp(), nullptr); } diff --git a/src/verilog.y b/src/verilog.y index f6dc00931..5ae9dfd9f 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7169,9 +7169,9 @@ bins_or_options: // ==IEEE: bins_or_options coverage_option { $$ = $1; } // // Can't use wildcardE as results in conflicts | yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, false}; + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, false, false, $8}; if ($3) binp->isArray(true); - $$ = binp; DEL($8); } + $$ = binp; } | yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE { // Check for automatic bins: bins auto[N] if (*$2 == "auto") { @@ -7184,13 +7184,13 @@ bins_or_options: // ==IEEE: bins_or_options } } | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, true, false}; + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, true, false, false, $8}; if ($3) binp->isArray(true); - $$ = binp; DEL($8); } + $$ = binp; } | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, true}; + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, true, false, $8}; if ($3) binp->isArray(true); - $$ = binp; DEL($8); } + $$ = binp; } | yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, false}; BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); @@ -7210,14 +7210,11 @@ bins_or_options: // ==IEEE: bins_or_options | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = new AstCoverBin{$3, *$3, $7, false, false, true}; - DEL($9); } + { $$ = new AstCoverBin{$3, *$3, $7, false, false, true, $9}; } | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = new AstCoverBin{$3, *$3, $7, true, false, true}; - DEL($9); } + { $$ = new AstCoverBin{$3, *$3, $7, true, false, true, $9}; } | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = new AstCoverBin{$3, *$3, $7, false, true, true}; - DEL($9); } + { $$ = new AstCoverBin{$3, *$3, $7, false, true, true, $9}; } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE diff --git a/test_regress/t/t_covergroup_iff.out b/test_regress/t/t_covergroup_iff.out index 6350a921d..a0b982b1e 100644 --- a/test_regress/t/t_covergroup_iff.out +++ b/test_regress/t/t_covergroup_iff.out @@ -1,4 +1,4 @@ -__vlAnonCG_cg.cp_a.one: 2 +__vlAnonCG_cg.cp_a.one: 1 __vlAnonCG_cg.cp_b.one: 2 __vlAnonCG_cg.cross_ab.one_x_one [cross]: 1 cg_and.cp_concat.b00: 0 @@ -8,6 +8,9 @@ cg_and.cp_concat.b11: 0 cg_array_iff.cp.arr[0]: 1 cg_array_iff.cp.arr[1]: 0 cg_array_iff.cp.arr[2]: 0 +cg_bin_cross.cp_a.one: 2 +cg_bin_cross.cp_b.one: 2 +cg_bin_cross.cross_ab.one_x_one [cross]: 1 cg_bitw.cp.seven: 1 cg_cross_coverpoint_iff.cp_a.one: 2 cg_cross_coverpoint_iff.cp_b.one: 2 @@ -29,5 +32,14 @@ cg_or.cp.lo: 1 cg_part.cp.one: 1 cg_rel.cp.five: 1 cg_rel.cp.two: 0 +cg_state_bin_iff.cp.grouped: 2 +cg_state_bin_iff.cp.ignored [ignore]: 1 +cg_state_bin_iff.cp.illegal [illegal]: 0 +cg_state_bin_iff.cp.scalar: 1 +cg_state_bin_iff.cp.values[0]: 1 +cg_state_bin_iff.cp.values[1]: 1 +cg_state_bin_iff.cp.wild: 2 +cg_state_bin_iff.cp.wild_ignored [ignore]: 2 +cg_state_bin_iff.cp.wild_illegal [illegal]: 0 cg_trans2_iff.cp.t2: 1 cg_trans3_iff.cp.t3: 1 diff --git a/test_regress/t/t_covergroup_iff.v b/test_regress/t/t_covergroup_iff.v index 310e0328d..d3b65359f 100644 --- a/test_regress/t/t_covergroup_iff.v +++ b/test_regress/t/t_covergroup_iff.v @@ -27,9 +27,11 @@ class CrossIffEmbedded; bit b; bit enabled; - // cross-level iff using an enclosing class member + // Bin- and cross-level iff using an enclosing class member. covergroup cg; - cp_a: coverpoint a {bins one = {1};} + cp_a: coverpoint a { + bins one = {1} iff (this.enabled); + } cp_b: coverpoint b {bins one = {1};} cross_ab: cross cp_a, cp_b iff (this.enabled); endgroup @@ -56,6 +58,10 @@ module t; logic [1:0] m_a; logic [1:0] m_b; int count; + logic [3:0] bin_value; + bit point_enable; + bit bin_enable; + logic [2:0] bin_mask; // iff on explicit value bins covergroup cg_iff; @@ -144,6 +150,29 @@ module t; cross cp_a, cp_b iff (!disable_cross) {} endgroup + covergroup cg_state_bin_iff; + cp: coverpoint bin_value iff (point_enable) { + bins scalar = {2} iff (bin_enable); + bins values[] = {0, 1} iff (bin_enable && bin_mask[0]); + bins grouped = {3, 4} iff (bin_enable && (bin_mask[2:1] != 0)); + wildcard bins wild = {4'b01?1} iff (bin_enable && !(bin_mask[2] == 0)); + ignore_bins ignored = {8} iff (bin_enable); + wildcard ignore_bins wild_ignored = {4'b10?1} iff (bin_enable && bin_mask[1]); + illegal_bins illegal = {12} iff (bin_enable); + wildcard illegal_bins wild_illegal = {4'b11?1} iff (bin_enable && bin_mask[2]); + } + endgroup + + covergroup cg_bin_cross with function sample (bit a, bit b, bit enable_a, bit enable_b); + cp_a: coverpoint a { + bins one = {1} iff (enable_a); + } + cp_b: coverpoint b { + bins one = {1} iff (enable_b); + } + cross_ab: cross cp_a, cp_b; + endgroup + cg_iff cg1 = new; cg_default_iff cg2 = new; cg_array_iff cg3 = new; @@ -158,6 +187,8 @@ module t; cg_cross_iff cx = new; cg_cross_iff_unnamed cxu = new; CrossIffEmbedded cxe = new; + cg_state_bin_iff state_bins = new; + cg_bin_cross cxb = new; initial begin cxc.sample(1, 1, 0, 1); @@ -172,6 +203,42 @@ module t; cxe.observe(1, 1, 0); cxe.observe(1, 1, 1); + cxb.sample(1, 1, 0, 0); + `checkr(cxb.get_inst_coverage(), 0.0); + cxb.sample(1, 1, 1, 0); + cxb.sample(1, 1, 0, 1); + `checkr(cxb.get_inst_coverage() < 100.0, 1); + cxb.sample(1, 1, 1, 1); + `checkr(cxb.get_inst_coverage(), 100.0); + cxb.sample(1, 1, 0, 0); + + // IEEE 1800-2023 19.5.1: iff gates sampling, not bin construction. + point_enable = 1; + bin_enable = 0; + bin_mask = 3'b111; + for (int i = 0; i < 8; ++i) begin + bin_value = 4'(i); + state_bins.sample(); + end + `checkr(state_bins.get_inst_coverage(), 0.0); + for (int i = 8; i < 16; ++i) begin + bin_value = 4'(i); + state_bins.sample(); + end + point_enable = 0; + bin_enable = 1; + for (int i = 0; i < 16; ++i) begin + bin_value = 4'(i); + state_bins.sample(); + end + `checkr(state_bins.get_inst_coverage(), 0.0); + point_enable = 1; + for (int i = 0; i < 12; ++i) begin + bin_value = 4'(i); + state_bins.sample(); + end + `checkr(state_bins.get_inst_coverage(), 100.0); + // Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded enable = 0; value = 1; diff --git a/test_regress/t/t_covergroup_iff_illegal.out b/test_regress/t/t_covergroup_iff_illegal.out new file mode 100644 index 000000000..3772f95f7 --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal.out @@ -0,0 +1,4 @@ +Disabled guards produced no illegal-bin errors. +[0] %Error: t_covergroup_iff_illegal.v:18: Assertion failed in top.t.cg.sample: Illegal bin 'scalar' hit in coverpoint 'cp' +%Error: t/t_covergroup_iff_illegal.v:18: Verilog $stop +Aborting... diff --git a/test_regress/t/t_covergroup_iff_illegal.py b/test_regress/t/t_covergroup_iff_illegal.py new file mode 100755 index 000000000..9b2e1a936 --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal.py @@ -0,0 +1,21 @@ +#!/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('vlt_all') + +test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1)) + +test.execute(fails=True, + check_finished=False, + all_run_flags=['+value=5'], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_iff_illegal.v b/test_regress/t/t_covergroup_iff_illegal.v new file mode 100644 index 000000000..7c99805bd --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__, `__LINE__, (gotv), (expv)); `stop; end while (0); +// verilog_format: on + +module t; + int sample_value; + + covergroup cg with function sample (bit [2:0] value, bit point_enable, bit bin_enable); + cp: coverpoint value iff (point_enable) { + bins good = {4}; + illegal_bins scalar = {5} iff (bin_enable); + illegal_bins values[] = {0, 1} iff (bin_enable); + wildcard illegal_bins wild = {3'b11?} iff (bin_enable); + } + endgroup + + cg inst = new; + + initial begin + if (!$value$plusargs("value=%d", sample_value)) `stop; + for (int i = 0; i < 8; ++i) inst.sample(3'(i), 1, 0); + for (int i = 0; i < 8; ++i) inst.sample(3'(i), 0, 1); +`ifdef VERILATOR + `checkd($c32("Verilated::threadContextp()->errorCount()"), 0); +`endif + $display("Disabled guards produced no illegal-bin errors."); + inst.sample(3'(sample_value), 1, 1); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_iff_illegal_array.out b/test_regress/t/t_covergroup_iff_illegal_array.out new file mode 100644 index 000000000..d8d89c415 --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal_array.out @@ -0,0 +1,4 @@ +Disabled guards produced no illegal-bin errors. +[0] %Error: t_covergroup_iff_illegal.v:19: Assertion failed in top.t.cg.sample: Illegal bin 'values' hit in coverpoint 'cp' +%Error: t/t_covergroup_iff_illegal.v:19: Verilog $stop +Aborting... diff --git a/test_regress/t/t_covergroup_iff_illegal_array.py b/test_regress/t/t_covergroup_iff_illegal_array.py new file mode 100755 index 000000000..19492e50a --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal_array.py @@ -0,0 +1,23 @@ +#!/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('vlt_all') + +test.top_filename = 't/t_covergroup_iff_illegal.v' + +test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1)) + +test.execute(fails=True, + check_finished=False, + all_run_flags=['+value=1'], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_iff_illegal_wildcard.out b/test_regress/t/t_covergroup_iff_illegal_wildcard.out new file mode 100644 index 000000000..f0951c9ad --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal_wildcard.out @@ -0,0 +1,4 @@ +Disabled guards produced no illegal-bin errors. +[0] %Error: t_covergroup_iff_illegal.v:20: Assertion failed in top.t.cg.sample: Illegal bin 'wild' hit in coverpoint 'cp' +%Error: t/t_covergroup_iff_illegal.v:20: Verilog $stop +Aborting... diff --git a/test_regress/t/t_covergroup_iff_illegal_wildcard.py b/test_regress/t/t_covergroup_iff_illegal_wildcard.py new file mode 100755 index 000000000..4cb91ab50 --- /dev/null +++ b/test_regress/t/t_covergroup_iff_illegal_wildcard.py @@ -0,0 +1,23 @@ +#!/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('vlt_all') + +test.top_filename = 't/t_covergroup_iff_illegal.v' + +test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1)) + +test.execute(fails=True, + check_finished=False, + all_run_flags=['+value=6'], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 103d7a65d..9a34fafbc 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -792,7 +792,7 @@ module Vt_debug_emitv_t; covergroup Vt_debug_emitv_cg_basic; function new; cp_sig: coverpoint cg_sig { - bins low = {['sh0:'sh3]}; + bins low = {['sh0:'sh3]} iff (cg_sig2[0]); bins high = {['sh4:'sh6]}; bins multi = {'sh0, 'sh1, 'sh2}; bins dflt = default; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index d783e1816..e7d425fd3 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -395,7 +395,7 @@ module t (/*AUTOARG*/ option.per_instance = 1; option.weight = 2; cp_sig: coverpoint cg_sig { - bins low = {[0:3]}; + bins low = {[0:3]} iff (cg_sig2[0]); bins high = {[4:6]}; bins multi = {0, 1, 2}; // multiple values in one bins (exercises EmitV range loop) bins dflt = default;