Support covergroup events with reference members or ref arguments (#8201)

This commit is contained in:
Marco Bartoli
2026-08-23 10:35:43 -04:00
committed by GitHub
parent d5f7878eb4
commit c4b84b20f7
7 changed files with 154 additions and 71 deletions
+31 -3
View File
@@ -34,6 +34,7 @@
#include "V3Graph.h"
#include <unordered_map>
#include <unordered_set>
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<const AstVar*> 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;
-13
View File
@@ -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;
+7 -2
View File
@@ -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());
+18
View File
@@ -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()
@@ -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
@@ -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
@@ -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