diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4659c85d1..7775b75fb 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2115,39 +2115,49 @@ class WidthVisitor final : public VNVisitor { // with a context so a bit/part-select (AstSel) is sized here; otherwise it would // reach assertAtExpr() with m_vup==null and fail as an internal error. userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p()); - userIterateAndNext(nodep->binsp(), nullptr); + // Bin values compare against the coverpoint expression + userIterateAndNext(nodep->binsp(), WidthVP{nodep->exprp()->dtypep(), BOTH}.p()); if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH); userIterateAndNext(nodep->optionsp(), nullptr); } - void widthCovergroupRanges(AstNode* rangesp) { + void widthCovergroupRanges(AstNode* rangesp, int fillWidth) { // Bin range/value entries are self-determined expressions (IEEE 1800-2023 // 19.5). Width each plain single-value entry self-determined so a referenced // parameter acquires a dtype, then constify so the reference folds to the AstConst // value that V3Covergroup requires. AstInsideRange entries fold their own bounds in // visit(AstInsideRange). + // '0/'1 entries then fill to fillWidth, the coverpoint width (IEEE 1800-2023 19.5.7). + const auto fill = [&](AstNode* nodep) { + if (!fillWidth) return; + AstNodeExpr* exprp = VN_AS(nodep, NodeExpr); + fixAutoExtend(exprp /*ref*/, fillWidth); + }; for (AstNode *nextp, *itemp = rangesp; itemp; itemp = nextp) { nextp = itemp->nextp(); - if (VN_IS(itemp, InsideRange)) { - userIterate(itemp, nullptr); + if (AstInsideRange* const rangep = VN_CAST(itemp, InsideRange)) { + userIterate(rangep, nullptr); + fill(rangep->lhsp()); + fill(rangep->rhsp()); } else { itemp = userIterateSubtreeReturnEdits(itemp, WidthVP{SELF, BOTH}.p()); - V3Const::constifyEdit(itemp); + fill(V3Const::constifyEdit(itemp)); } } } void visit(AstCoverBinsof* nodep) override { userIterateAndNext(nodep->pointp(), nullptr); - widthCovergroupRanges(nodep->rangesp()); + widthCovergroupRanges(nodep->rangesp(), 0); } void visit(AstCoverBin* nodep) override { - widthCovergroupRanges(nodep->rangesp()); + // No m_vup for a bin directly in a covergroup body (unsupported, already warned) + widthCovergroupRanges(nodep->rangesp(), m_vup ? m_vup->dtypep()->width() : 0); if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH); userIterateAndNext(nodep->arraySizep(), nullptr); - userIterateAndNext(nodep->transp(), nullptr); + userIterateAndNext(nodep->transp(), m_vup); } - void visit(AstCoverTransSet* nodep) override { userIterateAndNext(nodep->itemsp(), nullptr); } + void visit(AstCoverTransSet* nodep) override { userIterateAndNext(nodep->itemsp(), m_vup); } void visit(AstCoverTransItem* nodep) override { - userIterateAndNext(nodep->valuesp(), WidthVP{SELF, BOTH}.p()); + widthCovergroupRanges(nodep->valuesp(), m_vup ? m_vup->dtypep()->width() : 0); } void visit(AstPow* nodep) override { // Pow is special, output sign only depends on LHS sign, but diff --git a/test_regress/t/t_covergroup_bins_fill.out b/test_regress/t/t_covergroup_bins_fill.out new file mode 100644 index 000000000..b40e58119 --- /dev/null +++ b/test_regress/t/t_covergroup_bins_fill.out @@ -0,0 +1,12 @@ +cg.cp_b8.hi: 1 +cg.cp_b8.lo: 1 +cg.cp_insn.one: 0 +cg.cp_insn.ones: 1 +cg.cp_insn.rise: 1 +cg.cp_insn.zeros: 1 +cg.cp_mode.m: 2 +cg.cp_sb.m1: 2 +cg.cx.m_x_one [cross]: 0 +cg.cx.m_x_ones [cross]: 1 +cg.cx.m_x_rise [cross]: 1 +cg.cx.m_x_zeros [cross]: 1 diff --git a/test_regress/t/t_covergroup_bins_fill.py b/test_regress/t/t_covergroup_bins_fill.py new file mode 100755 index 000000000..c2434ea87 --- /dev/null +++ b/test_regress/t/t_covergroup_bins_fill.py @@ -0,0 +1,16 @@ +#!/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 + +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_bins_fill.v b/test_regress/t/t_covergroup_bins_fill.v new file mode 100644 index 000000000..a8fed77da --- /dev/null +++ b/test_regress/t/t_covergroup_bins_fill.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 David Harris +// SPDX-License-Identifier: CC0-1.0 + +// Test that '0 and '1 bin values fill to the coverpoint width + +module t; + bit [31:0] insn; + bit [7:0] b8; + bit [1:0] mode; + bit signed [7:0] sb; + + covergroup cg; + cp_insn: coverpoint insn { + bins zeros = {'0}; + bins ones = {'1}; + bins one = {1}; + bins rise = ('0 => '1); + } + cp_b8: coverpoint b8 { + bins hi = {[8'h80 : '1]}; + bins lo = {['0 : 8'h7f]}; + } + cp_sb: coverpoint sb {bins m1 = {'1};} + cp_mode: coverpoint mode {bins m = {3};} + cx: cross cp_mode, cp_insn; + endgroup + + cg cg_i = new; + + initial begin + mode = 3; + insn = 32'h0; + b8 = 8'h01; + sb = -1; + cg_i.sample(); + insn = 32'hffff_ffff; + b8 = 8'hff; + cg_i.sample(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule