mirror of
https://github.com/verilator/verilator.git
synced 2026-09-07 10:01:11 +02:00
Support covergroup events with reference members or ref arguments (#8201)
This commit is contained in:
+31
-3
@@ -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;
|
||||
|
||||
@@ -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
@@ -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());
|
||||
|
||||
Executable
+18
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user