diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 13cd270b4..8da88b5bb 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -34,6 +34,7 @@ #include "V3Graph.h" #include +#include VL_DEFINE_DEBUG_FUNCTIONS; @@ -670,6 +671,32 @@ public: //###################################################################### // Pass 2: inject automatic sample() calls for covergroup instances +class CovergroupEventBindVisitor final : public VNVisitor { + AstVarScope* const m_instancep; // Variable scope for the covergroup instance being sampled + std::unordered_set m_memberps; // Non-static variables in the covergroup class + + void visit(AstVarRef* nodep) override { + if (!m_memberps.count(nodep->varp())) return; + FileLine* const fl = nodep->fileline(); + AstMemberSel* const selp + = new AstMemberSel{fl, new AstVarRef{fl, m_instancep, VAccess::READ}, nodep->varp()}; + selp->access(nodep->access()); + nodep->replaceWith(selp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep) + : m_instancep{instancep} { + classp->foreachMember([&](AstClass* const, AstVar* const varp) { + if (!varp->isStatic()) m_memberps.emplace(varp); + }); + iterate(eventp); + } + ~CovergroupEventBindVisitor() override = default; +}; + class CovergroupInjectVisitor final : public VNVisitor { // NODE STATE (set by CovergroupCollectVisitor, consumed here) // AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class @@ -719,10 +746,11 @@ class CovergroupInjectVisitor final : public VNVisitor { cmethodCallp->dtypeSetVoid(); cmethodCallp->argTypes("vlSymsp"); - // Clone the sensitivity for this active block. - // V3Scope has already resolved all VarRefs in eventp, so the clone - // inherits correct varScopep values with no fixup needed. + // Clone the sensitivity for this active block. References to covergroup members need + // to select through this particular instance; all other VarRefs retain the VarScopes + // resolved by V3Scope. AstSenTree* senTreep = eventp->cloneTree(false); + CovergroupEventBindVisitor{senTreep, classp, nodep}; // Get or create the AstActive node for this sensitivity // senTreep is a template used by getActive() which clones it into the AstActive; diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index f838219a8..577bfae06 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -1707,11 +1707,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { return found; } - static bool hasInstanceMemberEventRef(const AstCovergroup* cgp) { - return cgp->eventp()->exists( - [](const AstVarRef* refp) { return isEnclosingInstanceVar(refp->varp()); }); - } - static bool parseEmbeddedEventExpr(AstNodeExpr* exprp, AstVar*& baseVarp, AstVar*& memberVarp) { if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { @@ -2085,14 +2080,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { itemp = nextp; continue; } - if (hasInstanceMemberEventRef(cgp)) { - cgp->v3warn(COVERIGN, "Unsupported: 'covergroup' clocking event " - "on member variable"); - hasUnsupportedEvent = true; - VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp); - itemp = nextp; - continue; - } // V3Active handles events that do not depend on an enclosing instance. UINFO(4, "Keeping covergroup event node for V3Active: " << nodep->name()); itemp = nextp; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 4d6f62ecc..7fb0d0bd3 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1596,15 +1596,20 @@ public: emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->matchp(), nullptr); } void visit(AstMemberSel* nodep) override { + const AstVar* const varp = nodep->varp(); + const bool dereferenceCovergroupRef + = varp->covergroupRefMember() && nodep->access().isReadOrRW(); + if (dereferenceCovergroupRef) putnbs(nodep, "(*"); iterateAndNextConstNull(nodep->fromp()); putnbs(nodep, "->"); - if (nodep->varp()->isIfaceRef()) { + if (varp->isIfaceRef()) { // varp is the __Viftop companion (e.g. "tx__Viftop"); use the // MemberSel name which matches the cell's C++ member (e.g. "tx"). puts(nodep->nameProtect()); } else { - puts(nodep->varp()->nameProtect()); + puts(varp->nameProtect()); } + if (dereferenceCovergroupRef) puts(")"); } void visit(AstStructSel* nodep) override { iterateAndNextConstNull(nodep->fromp()); diff --git a/test_regress/t/t_covergroup_member_event.py b/test_regress/t/t_covergroup_member_event.py new file mode 100755 index 000000000..cbf529a7d --- /dev/null +++ b/test_regress/t/t_covergroup_member_event.py @@ -0,0 +1,18 @@ +#!/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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile(verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_covergroup_member_event.v b/test_regress/t/t_covergroup_member_event.v new file mode 100644 index 000000000..24644f044 --- /dev/null +++ b/test_regress/t/t_covergroup_member_event.v @@ -0,0 +1,94 @@ +// 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 + +// Embedded covergroup events that cannot be approximated without --timing must +// emit COVERIGN rather than silently producing zero coverage. + +interface EventIf; + logic clk; +endinterface + +module t; + EventIf event_if(); + + logic ref_clock; + logic [1:0] ref_clocks; + virtual EventIf event_vif = event_if; + + class ExternalClk; + bit clk; + bit [3:0] value; + + covergroup cov_extclk @(posedge clk); + coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};} + endgroup + + function new(); + cov_extclk = new; + endfunction + endclass + + class Lvl; + bit ev; + endclass + + class Mid; + Lvl lvl; + endclass + + class ComplexEvt; + bit a; + Mid mid; + bit [3:0] value; + + covergroup cov_cplx @(posedge a or posedge mid.lvl.ev); + coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};} + endgroup + + function new(); + mid = new; + mid.lvl = new; + cov_cplx = new; + endfunction + endclass + + class StaticClk; + static bit clk; + static bit value; + endclass + + covergroup cov_static @(posedge StaticClk::clk); + coverpoint StaticClk::value; + endgroup + + covergroup cov_ref(ref logic event_ref) @(posedge event_ref); + coverpoint event_ref; + endgroup + + covergroup cov_ref_select(ref logic [1:0] event_refs) @(posedge event_refs[0]); + coverpoint event_refs[0]; + endgroup + + covergroup cov_ref_vif(ref virtual EventIf event_ref_vif) @(posedge event_ref_vif.clk); + coverpoint event_ref_vif.clk; + endgroup + + ExternalClk ec; + ComplexEvt cx; + cov_static static_cg = new; + cov_ref ref_cg = new(ref_clock); + cov_ref_select ref_select_cg = new(ref_clocks); + cov_ref_vif ref_vif_cg = new(event_vif); + + initial begin + ec = new; + cx = new; + StaticClk::value = 1; + StaticClk::clk = 1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_member_event_unsup.out b/test_regress/t/t_covergroup_member_event_unsup.out index f05a52266..44afaaabb 100644 --- a/test_regress/t/t_covergroup_member_event_unsup.out +++ b/test_regress/t/t_covergroup_member_event_unsup.out @@ -1,22 +1,10 @@ -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:25:29: Unsupported: 'covergroup' clocking event signal has no assignment within the enclosing class; no coverage sampled. Use --timing for full support. - 25 | covergroup cov_extclk @(posedge clk); +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:15:29: Unsupported: 'covergroup' clocking event signal has no assignment within the enclosing class; no coverage sampled. Use --timing for full support. + 15 | covergroup cov_extclk @(posedge clk); | ^~~~~~~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:47:5: Unsupported: 'covergroup' clocking event on complex member expression; use --timing for full support. +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:37:5: Unsupported: 'covergroup' clocking event on complex member expression; use --timing for full support. : ... note: In instance 't' - 47 | covergroup cov_cplx @(posedge a or posedge mid.lvl.ev); + 37 | covergroup cov_cplx @(posedge a or posedge mid.lvl.ev); | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:67:3: Unsupported: 'covergroup' clocking event on member variable - : ... note: In instance 't' - 67 | covergroup cov_ref(ref logic event_ref) @(posedge event_ref); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:71:3: Unsupported: 'covergroup' clocking event on member variable - : ... note: In instance 't' - 71 | covergroup cov_ref_select(ref logic [1:0] event_refs) @(posedge event_refs[0]); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:75:3: Unsupported: 'covergroup' clocking event on member variable - : ... note: In instance 't' - 75 | covergroup cov_ref_vif(ref virtual EventIf event_ref_vif) @(posedge event_ref_vif.clk); - | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_member_event_unsup.v b/test_regress/t/t_covergroup_member_event_unsup.v index 69df44c79..e2ff92710 100644 --- a/test_regress/t/t_covergroup_member_event_unsup.v +++ b/test_regress/t/t_covergroup_member_event_unsup.v @@ -7,17 +7,7 @@ // Embedded covergroup events that cannot be approximated without --timing must // emit COVERIGN rather than silently producing zero coverage. -interface EventIf; - logic clk; -endinterface - module t; - EventIf event_if(); - - logic ref_clock; - logic [1:0] ref_clocks; - virtual EventIf event_vif = event_if; - class ExternalClk; bit clk; bit [3:0] value; @@ -55,39 +45,12 @@ module t; endfunction endclass - class StaticClk; - static bit clk; - static bit value; - endclass - - covergroup cov_static @(posedge StaticClk::clk); - coverpoint StaticClk::value; - endgroup - - covergroup cov_ref(ref logic event_ref) @(posedge event_ref); - coverpoint event_ref; - endgroup - - covergroup cov_ref_select(ref logic [1:0] event_refs) @(posedge event_refs[0]); - coverpoint event_refs[0]; - endgroup - - covergroup cov_ref_vif(ref virtual EventIf event_ref_vif) @(posedge event_ref_vif.clk); - coverpoint event_ref_vif.clk; - endgroup - ExternalClk ec; ComplexEvt cx; - cov_static static_cg = new; - cov_ref ref_cg = new(ref_clock); - cov_ref_select ref_select_cg = new(ref_clocks); - cov_ref_vif ref_vif_cg = new(event_vif); initial begin ec = new; cx = new; - StaticClk::value = 1; - StaticClk::clk = 1; $write("*-* All Finished *-*\n"); $finish; end