parent
56accae45e
commit
7e116ca601
|
|
@ -584,6 +584,7 @@ class AstClassRefDType final : public AstNodeDType {
|
|||
//
|
||||
// @astgen ptr := m_classp : Optional[AstClass] // data type pointed to, BELOW the AstTypedef
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
|
||||
bool m_rawPointer = false; // Emit as a non-owning C++ pointer rather than VlClassRef
|
||||
public:
|
||||
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
|
||||
: ASTGEN_SUPER_ClassRefDType(fl)
|
||||
|
|
@ -595,7 +596,8 @@ public:
|
|||
// METHODS
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep
|
||||
&& m_rawPointer == asamep->m_rawPointer);
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
|
|
@ -613,6 +615,9 @@ public:
|
|||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
||||
void classp(AstClass* nodep) { m_classp = nodep; }
|
||||
bool rawPointer() const { return m_rawPointer; }
|
||||
void rawPointer(bool flag) { m_rawPointer = flag; }
|
||||
static void selfTest();
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstConstDType final : public AstNodeDType {
|
||||
|
|
|
|||
|
|
@ -2897,6 +2897,7 @@ class AstClass final : public AstNodeModule {
|
|||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
|
|
@ -2921,6 +2922,10 @@ public:
|
|||
bool timescaleMatters() const override { return false; }
|
||||
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstClass* covergroupEnclosingClassp() const VL_MT_STABLE {
|
||||
return m_covergroupEnclosingClassp;
|
||||
}
|
||||
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
|
||||
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isCovergroup() const { return m_covergroup; }
|
||||
|
|
|
|||
|
|
@ -1194,7 +1194,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCUtil::prefixNameProtect(adtypep) + ">";
|
||||
const string className = EmitCUtil::prefixNameProtect(adtypep);
|
||||
info.m_type = adtypep->rawPointer() ? className + "*" : "VlClassRef<" + className + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
|
|
@ -2196,8 +2197,22 @@ void AstClassRefDType::dump(std::ostream& str) const {
|
|||
} else {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
if (rawPointer()) str << " [RAWPTR]";
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, rawPointer);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassRefDType::selfTest() {
|
||||
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
|
||||
AstClassRefDType* const owningp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
AstClassRefDType* const rawp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
rawp->rawPointer(true);
|
||||
UASSERT_OBJ(!owningp->sameNode(rawp) && !rawp->sameNode(owningp) && rawp->sameNode(rawp), rawp,
|
||||
"Raw class pointer must have distinct type identity");
|
||||
VL_DO_DANGLING(owningp->deleteTree(), owningp);
|
||||
VL_DO_DANGLING(rawp->deleteTree(), rawp);
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "V3MemberMap.h"
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
|
||||
// STATE
|
||||
AstClass* m_covergroupp = nullptr; // Current covergroup being processed
|
||||
AstClass* m_enclosingClassp = nullptr; // Class lexically enclosing the covergroup (if any)
|
||||
AstFunc* m_sampleFuncp = nullptr; // Current sample() function
|
||||
AstFunc* m_constructorp = nullptr; // Current constructor
|
||||
std::vector<AstCoverpoint*> m_coverpoints; // Coverpoints in current covergroup
|
||||
|
|
@ -68,6 +70,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
, crossBins{cb} {}
|
||||
};
|
||||
std::vector<BinInfo> m_binInfos; // All bins in current covergroup
|
||||
|
||||
std::set<std::string> m_crossedCpNames; // Coverpoints referenced by a cross (kept legacy)
|
||||
std::vector<AstVar*> m_convCpVars; // VlCoverpoint members of converted coverpoints
|
||||
AstCDType* m_vlCoverpointDTypep = nullptr; // Shared "VlCoverpoint" C++ member type
|
||||
|
|
@ -1692,38 +1695,185 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
// VISITORS
|
||||
AstNode* findUnsupportedCoverpointRef(AstClass* cgClassp) {
|
||||
// An embedded covergroup is lowered into a sibling AstClass that (currently) has
|
||||
// no handle to the enclosing object. Identify refs to the containing context
|
||||
// or a formal param and flag as unsupported
|
||||
static bool isEnclosingInstanceVar(const AstVar* varp) {
|
||||
return varp->isClassMember() && !varp->lifetime().isStatic() && !varp->isParam();
|
||||
}
|
||||
|
||||
void rewriteThisRef(AstThisRef* refp, AstVar* handleVarp) {
|
||||
const AstClassRefDType* const refDTypep
|
||||
= VN_CAST(refp->dtypep()->skipRefp(), ClassRefDType);
|
||||
UASSERT_OBJ(refDTypep && refDTypep->classp() == m_covergroupp, refp,
|
||||
"Unexpected this reference in embedded covergroup");
|
||||
AstNodeExpr* const newp = new AstVarRef{refp->fileline(), handleVarp, VAccess::READ};
|
||||
refp->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
|
||||
void rewriteVarRef(AstVarRef* refp, AstVar* handleVarp) {
|
||||
FileLine* const fl = refp->fileline();
|
||||
AstMemberSel* const selp
|
||||
= new AstMemberSel{fl, new AstVarRef{fl, handleVarp, VAccess::READ}, refp->varp()};
|
||||
selp->access(refp->access());
|
||||
refp->replaceWith(selp);
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
|
||||
void deleteCoverageItems() {
|
||||
for (AstCoverpoint* const cpp : m_coverpoints) {
|
||||
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
||||
}
|
||||
for (AstCoverCross* const crossp : m_coverCrosses) {
|
||||
VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp);
|
||||
}
|
||||
}
|
||||
|
||||
class FormalRefVisitor final : public VNVisitor {
|
||||
const std::set<const AstVar*>& m_constructorArgs;
|
||||
AstMemberSel* m_memberSelp = nullptr;
|
||||
AstNode* m_offenderp = nullptr;
|
||||
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_offenderp) return;
|
||||
VL_RESTORER(m_memberSelp);
|
||||
if (!m_memberSelp) m_memberSelp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (m_memberSelp && m_constructorArgs.count(nodep->varp())) {
|
||||
m_offenderp = m_memberSelp;
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (!m_offenderp) iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit FormalRefVisitor(const std::set<const AstVar*>& constructorArgs)
|
||||
: m_constructorArgs{constructorArgs} {}
|
||||
void scan(AstNode* nodep) {
|
||||
if (nodep && !m_offenderp) iterate(nodep);
|
||||
}
|
||||
AstNode* offenderp() const { return m_offenderp; }
|
||||
};
|
||||
|
||||
AstNode* findUnsupportedFormalRef() {
|
||||
std::set<const AstVar*> constructorArgs;
|
||||
for (AstNode* stmtp = m_constructorp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (varp->isIO()) constructorArgs.insert(varp);
|
||||
}
|
||||
}
|
||||
FormalRefVisitor visitor{constructorArgs};
|
||||
for (AstCoverpoint* const cpp : m_coverpoints) {
|
||||
visitor.scan(cpp->exprp());
|
||||
visitor.scan(cpp->iffp());
|
||||
}
|
||||
return visitor.offenderp();
|
||||
}
|
||||
|
||||
AstVarRef* installEnclosingBackPointer() {
|
||||
// Simple-case support for embedded covergroups (IEEE 1800-2023 19.4) whose
|
||||
// coverpoints reference members of the enclosing class ("Class members can be used
|
||||
// in coverpoint expressions"). The covergroup is lowered into a sibling class with
|
||||
// no implicit handle to the enclosing object, so such references would emit
|
||||
// uncompilable C++. Add an explicit back-pointer member to the enclosing instance,
|
||||
// route the member references through it, and initialize it right after the
|
||||
// 'cgvar = new' construction. The enclosing member values are only read in
|
||||
// sample(), which runs after construction, so this ordering is safe. Returns an invalid
|
||||
// reference if an outer class member cannot be reached; otherwise returns an empty result.
|
||||
if (!m_enclosingClassp) return nullptr; // Offending refs require an enclosing class
|
||||
|
||||
AstVarRef* invalidp = nullptr;
|
||||
AstNode* offenderp = nullptr;
|
||||
std::set<const AstVar*> ownVars;
|
||||
for (AstNode* itemp = cgClassp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
for (AstNode* itemp = m_covergroupp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(itemp, Var)) ownVars.insert(varp);
|
||||
}
|
||||
// Flag non-static enclosing-class members reached without a handle as unsupported
|
||||
AstNode* offenderp = nullptr;
|
||||
const auto scanEnclosing = [&](AstNode* rootp) {
|
||||
std::set<const AstVar*> enclosingVars;
|
||||
m_enclosingClassp->foreachMember([&](AstClass* const, AstVar* const varp) {
|
||||
if (isEnclosingInstanceVar(varp)) enclosingVars.insert(varp);
|
||||
});
|
||||
|
||||
std::vector<AstVarRef*> refsToRewrite;
|
||||
std::vector<AstThisRef*> thisRefsToRewrite;
|
||||
const auto scan = [&](AstNode* rootp) {
|
||||
rootp->foreach([&](AstVarRef* refp) {
|
||||
if (offenderp) return;
|
||||
if (invalidp) return;
|
||||
const AstVar* const varp = refp->varp();
|
||||
if (varp->isClassMember() && !ownVars.count(varp)) offenderp = refp;
|
||||
if (!isEnclosingInstanceVar(varp) || ownVars.count(varp)) return;
|
||||
if (!enclosingVars.count(varp)) {
|
||||
invalidp = refp;
|
||||
return;
|
||||
}
|
||||
refsToRewrite.push_back(refp);
|
||||
if (!offenderp) offenderp = refp;
|
||||
});
|
||||
if (invalidp) return;
|
||||
rootp->foreach([&](AstThisRef* refp) {
|
||||
const AstClassRefDType* const refDTypep
|
||||
= VN_CAST(refp->dtypep()->skipRefp(), ClassRefDType);
|
||||
if (refDTypep && refDTypep->classp() == m_covergroupp) {
|
||||
thisRefsToRewrite.push_back(refp);
|
||||
if (!offenderp) offenderp = refp;
|
||||
}
|
||||
});
|
||||
};
|
||||
for (AstCoverpoint* cpp : m_coverpoints) scanEnclosing(cpp);
|
||||
for (AstCoverCross* crossp : m_coverCrosses) scanEnclosing(crossp);
|
||||
if (offenderp) return offenderp;
|
||||
// Flag references to covergroup formal parameters as currently unsupported
|
||||
const auto scanHandleDeref = [&](AstNode* rootp) {
|
||||
if (!rootp) return;
|
||||
rootp->foreach([&](AstMemberSel* selp) {
|
||||
if (!offenderp) offenderp = selp;
|
||||
});
|
||||
};
|
||||
for (AstCoverpoint* cpp : m_coverpoints) {
|
||||
scanHandleDeref(cpp->exprp());
|
||||
scanHandleDeref(cpp->iffp());
|
||||
for (AstCoverpoint* const cpp : m_coverpoints) scan(cpp);
|
||||
for (AstCoverCross* const crossp : m_coverCrosses) scan(crossp);
|
||||
if (invalidp || !offenderp) return invalidp;
|
||||
|
||||
AstVar* embeddedVarp = nullptr;
|
||||
for (AstNode* itemp = m_enclosingClassp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
AstVar* const varp = VN_CAST(itemp, Var);
|
||||
if (!varp) continue;
|
||||
const AstClassRefDType* const refp
|
||||
= VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType);
|
||||
if (refp && refp->classp() == m_covergroupp) {
|
||||
embeddedVarp = varp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return offenderp;
|
||||
UASSERT_OBJ(embeddedVarp, m_covergroupp, "Embedded covergroup variable not found");
|
||||
|
||||
std::vector<AstNodeAssign*> constructps;
|
||||
AstFunc* const enclosingNewp
|
||||
= VN_CAST(m_memberMap.findMember(m_enclosingClassp, "new"), Func);
|
||||
if (enclosingNewp) {
|
||||
enclosingNewp->foreach([&](AstNodeAssign* asgnp) {
|
||||
const AstNew* const newp = VN_CAST(asgnp->rhsp(), New);
|
||||
const AstVarRef* const lhsRefp = VN_CAST(asgnp->lhsp(), VarRef);
|
||||
if (newp && lhsRefp && lhsRefp->varp() == embeddedVarp) {
|
||||
const AstClassRefDType* const refp = VN_CAST(newp->dtypep(), ClassRefDType);
|
||||
if (refp && refp->classp() == m_covergroupp) constructps.push_back(asgnp);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Commit: add the back-pointer member, rewrite the references, initialize the handle.
|
||||
FileLine* const fl = m_covergroupp->fileline();
|
||||
AstClassRefDType* const enclDTypep = new AstClassRefDType{fl, m_enclosingClassp, nullptr};
|
||||
enclDTypep->rawPointer(true);
|
||||
v3Global.rootp()->typeTablep()->addTypesp(enclDTypep);
|
||||
AstVar* const handleVarp
|
||||
= new AstVar{fl, VVarType::MEMBER, "__Vcg_enclosingp", enclDTypep};
|
||||
handleVarp->isStatic(false);
|
||||
m_covergroupp->addMembersp(handleVarp);
|
||||
|
||||
// Route each enclosing-member reference through the back-pointer: 'm' -> 'h.m'.
|
||||
for (AstVarRef* const refp : refsToRewrite) { rewriteVarRef(refp, handleVarp); }
|
||||
for (AstThisRef* const refp : thisRefsToRewrite) { rewriteThisRef(refp, handleVarp); }
|
||||
|
||||
// Initialize the raw back-pointer after each construction. With no construction site,
|
||||
// the embedded covergroup handle remains null, so no back-pointer is observed.
|
||||
for (AstNodeAssign* const constructp : constructps) {
|
||||
FileLine* const cfl = constructp->fileline();
|
||||
AstMemberSel* const lhsp
|
||||
= new AstMemberSel{cfl, constructp->lhsp()->cloneTree(false), handleVarp};
|
||||
lhsp->access(VAccess::WRITE);
|
||||
AstCExpr* const thisp = new AstCExpr{cfl, "this"};
|
||||
thisp->dtypep(enclDTypep);
|
||||
constructp->addNextHere(new AstAssign{cfl, lhsp, thisp});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void visit(AstClass* nodep) override {
|
||||
|
|
@ -1785,12 +1935,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
// but still clean up coverpoints so they don't reach downstream passes
|
||||
if (hasUnsupportedEvent) {
|
||||
iterateChildren(nodep);
|
||||
for (AstCoverpoint* cpp : m_coverpoints) {
|
||||
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
||||
}
|
||||
for (AstCoverCross* crossp : m_coverCrosses) {
|
||||
VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp);
|
||||
}
|
||||
deleteCoverageItems();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1806,35 +1951,35 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
|
||||
iterateChildren(nodep);
|
||||
|
||||
// Identify embedded covergroup refs to enclosing class members or
|
||||
// covergroup formal parameters and flag as currently unsupported.
|
||||
if (AstNode* const offenderp = findUnsupportedCoverpointRef(nodep)) {
|
||||
const bool viaHandle = VN_IS(offenderp, MemberSel);
|
||||
offenderp->v3warn(COVERIGN,
|
||||
"Unsupported: 'covergroup' coverpoint "
|
||||
<< (viaHandle ? "dereferencing a class handle member "
|
||||
"(parameterized covergroup)"
|
||||
: "referencing enclosing class member")
|
||||
<< "; ignoring covergroup " << nodep->prettyNameQ());
|
||||
for (AstCoverpoint* cpp : m_coverpoints) {
|
||||
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
||||
}
|
||||
for (AstCoverCross* crossp : m_coverCrosses) {
|
||||
VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp);
|
||||
}
|
||||
if (AstNode* const offenderp = findUnsupportedFormalRef()) {
|
||||
offenderp->v3warn(COVERIGN, "Unsupported: 'covergroup' coverpoint dereferencing a "
|
||||
"class handle member; ignoring covergroup "
|
||||
<< nodep->prettyNameQ());
|
||||
deleteCoverageItems();
|
||||
return;
|
||||
}
|
||||
|
||||
// Embedded covergroups (IEEE 1800-2023 19.4): coverpoints, iff expressions, and
|
||||
// crosses may reference members of the enclosing class. The covergroup is lowered
|
||||
// into a sibling class with no implicit handle to the enclosing instance. Install
|
||||
// an explicit back-pointer and route the references through it.
|
||||
if (AstVarRef* const invalidp = installEnclosingBackPointer()) {
|
||||
invalidp->v3error("Non-static member "
|
||||
<< invalidp->varp()->prettyNameQ()
|
||||
<< " of an outer class requires an explicit "
|
||||
"object handle (IEEE 1800-2023 8.23).");
|
||||
deleteCoverageItems();
|
||||
return;
|
||||
}
|
||||
processCovergroup();
|
||||
// Remove lowered coverpoints/crosses from the class - they have been
|
||||
// fully translated into C++ code and must not reach downstream passes
|
||||
for (AstCoverpoint* cpp : m_coverpoints) {
|
||||
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
||||
}
|
||||
for (AstCoverCross* crossp : m_coverCrosses) {
|
||||
VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp);
|
||||
}
|
||||
deleteCoverageItems();
|
||||
} else {
|
||||
// Track the lexically enclosing class so a nested covergroup can resolve
|
||||
// references to the enclosing object's members (installEnclosingBackPointer).
|
||||
VL_RESTORER(m_enclosingClassp);
|
||||
m_enclosingClassp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -541,8 +541,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
|||
depth + 1, suffix + ".atDefault()", nullptr);
|
||||
} else if (VN_IS(dtypep, CDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (const AstClassRefDType* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
return adtypep->rawPointer() ? varNameProtected + suffix + " = nullptr;\n" : "";
|
||||
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
||||
return varNameProtected + suffix + " = nullptr;\n";
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
|
|
|
|||
|
|
@ -249,8 +249,37 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
||||
if (!classp->isInterfaceClass() && !classp->isVirtual()) {
|
||||
decorateFirst(first, section);
|
||||
putns(classp, "VlClass* clone() const { return new "
|
||||
+ EmitCUtil::prefixNameProtect(classp) + "(*this); }\n");
|
||||
using EmbeddedCovergroupVar = std::pair<const AstClass*, const AstVar*>;
|
||||
std::vector<EmbeddedCovergroupVar> embeddedCovergroupVars;
|
||||
const auto hasEnclosingBackPointer = [](const AstClass* covergroupp) {
|
||||
return covergroupp->exists([](const AstVar* const varp) {
|
||||
const AstClassRefDType* const refp
|
||||
= VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType);
|
||||
return refp && refp->rawPointer();
|
||||
});
|
||||
};
|
||||
const_cast<AstClass*>(classp)->foreachMember(
|
||||
[&](AstClass* const memberClassp, AstVar* const varp) {
|
||||
const AstClassRefDType* const refp
|
||||
= VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType);
|
||||
if (refp && refp->classp()->isCovergroup()
|
||||
&& hasEnclosingBackPointer(refp->classp())) {
|
||||
embeddedCovergroupVars.emplace_back(memberClassp, varp);
|
||||
}
|
||||
});
|
||||
const string className = EmitCUtil::prefixNameProtect(classp);
|
||||
if (embeddedCovergroupVars.empty()) {
|
||||
putns(classp,
|
||||
"VlClass* clone() const { return new " + className + "(*this); }\n");
|
||||
} else {
|
||||
putns(classp, "VlClass* clone() const { " + className + "* const clonep = new "
|
||||
+ className + "(*this); ");
|
||||
for (const EmbeddedCovergroupVar& item : embeddedCovergroupVars) {
|
||||
puts("clonep->" + EmitCUtil::prefixNameProtect(item.first)
|
||||
+ "::" + item.second->nameProtect() + " = VlNull{}; ");
|
||||
}
|
||||
puts("return clonep; }\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4338,6 +4338,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else {
|
||||
foundp = m_ds.m_dotSymp->findIdFlat(nodep->name());
|
||||
}
|
||||
if (!foundp && m_ds.m_dotp && VN_IS(m_ds.m_dotp->lhsp(), ParseRef)
|
||||
&& m_ds.m_dotp->lhsp()->name() == "this") {
|
||||
const AstClass* const classp = VN_CAST(m_ds.m_dotSymp->nodep(), Class);
|
||||
if (classp && classp->isCovergroup() && classp->covergroupEnclosingClassp()) {
|
||||
VSymEnt* const enclosingClassSymp
|
||||
= m_statep->getNodeSym(classp->covergroupEnclosingClassp());
|
||||
foundp = enclosingClassSymp->findIdFallback(nodep->name());
|
||||
if (foundp) m_ds.m_dotSymp = enclosingClassSymp;
|
||||
}
|
||||
}
|
||||
// If not found in modport, check interface fallback for parameters and typedefs.
|
||||
// Parameters and typedefs are always visible through a modport (IEEE 1800-2023 25.5).
|
||||
// This mirrors the VarXRef modport parameter fallback in visit(AstVarXRef).
|
||||
|
|
@ -5848,7 +5858,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_insideClassExtParam);
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
m_insideClassExtParam = false;
|
||||
m_insideClassExtParam = nodep->isCovergroup() && nodep->covergroupEnclosingClassp()
|
||||
&& m_extendsParam.find(nodep->covergroupEnclosingClassp())
|
||||
!= m_extendsParam.end();
|
||||
// Until overridden by a SCOPE
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep);
|
||||
m_modp = nodep;
|
||||
|
|
|
|||
|
|
@ -1285,6 +1285,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
const string libname = m_modp->libname();
|
||||
AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname};
|
||||
cgClassp->isCovergroup(true);
|
||||
cgClassp->covergroupEnclosingClassp(VN_CAST(m_modp, Class));
|
||||
v3Global.useCovergroup(true);
|
||||
|
||||
// Clocking event: unlink before deleteTree, attach as AstCovergroup child on class
|
||||
|
|
|
|||
|
|
@ -119,7 +119,11 @@ private:
|
|||
nodep->v3fatalSrc("ref to unhandled definition type " << defp->prettyTypeName());
|
||||
}
|
||||
if (local || prot) {
|
||||
const auto refClassp = VN_CAST(m_modp, Class);
|
||||
// In case of covergroup, the reference is to the enclosing class, not the covergroup
|
||||
// itself
|
||||
const AstClass* refClassp = VN_CAST(m_modp, Class);
|
||||
if (refClassp && refClassp->isCovergroup())
|
||||
refClassp = refClassp->covergroupEnclosingClassp();
|
||||
const char* how = nullptr;
|
||||
// Inner nested classes can access `local` or `protected` members of their outer class
|
||||
const auto nestedAccess = [refClassp](const AstClass*, const AstNode* memberp) {
|
||||
|
|
|
|||
|
|
@ -736,6 +736,7 @@ static bool verilate(const string& argString) {
|
|||
// and after removing files as may make debug output)
|
||||
VBasicDTypeKwd::selfTest();
|
||||
if (v3Global.opt.debugSelfTest()) {
|
||||
AstClassRefDType::selfTest();
|
||||
V3Os::selfTest();
|
||||
V3Number::selfTest();
|
||||
VCMethod::selfTest();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
]},
|
||||
{"type":"PACKAGE","name":"$unit","addr":"(E)","loc":"a,0:0,0:0","origName":"__024unit","verilogName":"\\$unit ","level":2,"inLibrary":true,"timeunit":"1ps","inlinesp": [],
|
||||
"stmtsp": [
|
||||
{"type":"CLASS","name":"Packet","addr":"(O)","loc":"d,7:1,7:6","origName":"Packet","verilogName":"Packet","level":3,"timeunit":"1ps","classOrPackagep":"UNLINKED","inlinesp": [],
|
||||
{"type":"CLASS","name":"Packet","addr":"(O)","loc":"d,7:1,7:6","origName":"Packet","verilogName":"Packet","level":3,"timeunit":"1ps","classOrPackagep":"UNLINKED","covergroupEnclosingClassp":"UNLINKED","inlinesp": [],
|
||||
"stmtsp": [
|
||||
{"type":"VAR","name":"header","addr":"(P)","loc":"d,8:12,8:18","dtypep":"(Q)","origName":"header","verilogName":"header","direction":"NONE","lifetime":"VAUTOMI","varType":"MEMBER","dtypeName":"int","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
|
||||
{"type":"VAR","name":"length","addr":"(R)","loc":"d,9:12,9:18","dtypep":"(Q)","origName":"length","verilogName":"length","direction":"NONE","lifetime":"VAUTOMI","varType":"MEMBER","dtypeName":"int","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
GlobalCg.cp_global.auto_0: 0
|
||||
GlobalCg.cp_global.auto_1: 0
|
||||
GlobalCg.cp_global.auto_10: 0
|
||||
GlobalCg.cp_global.auto_11: 0
|
||||
GlobalCg.cp_global.auto_12: 0
|
||||
GlobalCg.cp_global.auto_13: 0
|
||||
GlobalCg.cp_global.auto_14: 0
|
||||
GlobalCg.cp_global.auto_15: 0
|
||||
GlobalCg.cp_global.auto_2: 0
|
||||
GlobalCg.cp_global.auto_3: 0
|
||||
GlobalCg.cp_global.auto_4: 0
|
||||
GlobalCg.cp_global.auto_5: 0
|
||||
GlobalCg.cp_global.auto_6: 0
|
||||
GlobalCg.cp_global.auto_7: 0
|
||||
GlobalCg.cp_global.auto_8: 0
|
||||
GlobalCg.cp_global.auto_9: 0
|
||||
__vlAnonCG_base_cg.cp_base.hi: 8
|
||||
__vlAnonCG_base_cg.cp_base.lo: 8
|
||||
__vlAnonCG_branch_cg.cp_branch.hi: 8
|
||||
__vlAnonCG_branch_cg.cp_branch.lo: 8
|
||||
__vlAnonCG_cg.cp_base.auto_0: 0
|
||||
__vlAnonCG_cg.cp_base.auto_1: 0
|
||||
__vlAnonCG_cg.cp_base.auto_10: 0
|
||||
__vlAnonCG_cg.cp_base.auto_11: 0
|
||||
__vlAnonCG_cg.cp_base.auto_12: 0
|
||||
__vlAnonCG_cg.cp_base.auto_13: 0
|
||||
__vlAnonCG_cg.cp_base.auto_14: 0
|
||||
__vlAnonCG_cg.cp_base.auto_15: 0
|
||||
__vlAnonCG_cg.cp_base.auto_2: 0
|
||||
__vlAnonCG_cg.cp_base.auto_3: 0
|
||||
__vlAnonCG_cg.cp_base.auto_4: 0
|
||||
__vlAnonCG_cg.cp_base.auto_5: 0
|
||||
__vlAnonCG_cg.cp_base.auto_6: 0
|
||||
__vlAnonCG_cg.cp_base.auto_7: 0
|
||||
__vlAnonCG_cg.cp_base.auto_8: 0
|
||||
__vlAnonCG_cg.cp_base.auto_9: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_0: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_1: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_10: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_11: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_12: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_13: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_14: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_15: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_2: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_3: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_4: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_5: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_6: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_7: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_8: 0
|
||||
__vlAnonCG_cg.cp_derived.auto_9: 0
|
||||
__vlAnonCG_copy_cg.cp_copy.hi: 0
|
||||
__vlAnonCG_copy_cg.cp_copy.lo: 1
|
||||
__vlAnonCG_derived_cg.cp_cross.hi_x_hi [cross]: 0
|
||||
__vlAnonCG_derived_cg.cp_cross.hi_x_lo [cross]: 8
|
||||
__vlAnonCG_derived_cg.cp_cross.lo_x_hi [cross]: 8
|
||||
__vlAnonCG_derived_cg.cp_cross.lo_x_lo [cross]: 0
|
||||
__vlAnonCG_derived_cg.cp_derived.hi: 8
|
||||
__vlAnonCG_derived_cg.cp_derived.lo: 8
|
||||
__vlAnonCG_derived_cg.cp_inherited.hi: 8
|
||||
__vlAnonCG_derived_cg.cp_inherited.lo: 8
|
||||
__vlAnonCG_derived_cg.cp_this_inherited.hi: 8
|
||||
__vlAnonCG_derived_cg.cp_this_inherited.lo: 8
|
||||
__vlAnonCG_first_cg.cp_first.hi: 8
|
||||
__vlAnonCG_first_cg.cp_first.lo: 8
|
||||
__vlAnonCG_leaf_cg.cp_cross.hi_x_hi [cross]: 0
|
||||
__vlAnonCG_leaf_cg.cp_cross.hi_x_lo [cross]: 8
|
||||
__vlAnonCG_leaf_cg.cp_cross.lo_x_hi [cross]: 8
|
||||
__vlAnonCG_leaf_cg.cp_cross.lo_x_lo [cross]: 0
|
||||
__vlAnonCG_leaf_cg.cp_leaf.hi: 8
|
||||
__vlAnonCG_leaf_cg.cp_leaf.lo: 8
|
||||
__vlAnonCG_leaf_cg.cp_root.hi: 8
|
||||
__vlAnonCG_leaf_cg.cp_root.lo: 8
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.hi_x_auto_0 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.hi_x_auto_1 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.hi_x_auto_2 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.hi_x_auto_3 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.lo_x_auto_0 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.lo_x_auto_1 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.lo_x_auto_2 [cross]: 2
|
||||
__vlAnonCG_mon_cg.addr_x_op_b.lo_x_auto_3 [cross]: 2
|
||||
__vlAnonCG_mon_cg.cp_addr.hi: 8
|
||||
__vlAnonCG_mon_cg.cp_addr.lo: 8
|
||||
__vlAnonCG_mon_cg.cp_enabled.hi: 8
|
||||
__vlAnonCG_mon_cg.cp_enabled.lo: 0
|
||||
__vlAnonCG_mon_cg.cp_inner.hi: 8
|
||||
__vlAnonCG_mon_cg.cp_inner.lo: 8
|
||||
__vlAnonCG_mon_cg.cp_op_a.hi: 8
|
||||
__vlAnonCG_mon_cg.cp_op_a.lo: 8
|
||||
__vlAnonCG_mon_cg.cp_op_b.auto_0: 4
|
||||
__vlAnonCG_mon_cg.cp_op_b.auto_1: 4
|
||||
__vlAnonCG_mon_cg.cp_op_b.auto_2: 4
|
||||
__vlAnonCG_mon_cg.cp_op_b.auto_3: 4
|
||||
__vlAnonCG_nested_cg.cp_container.hi: 8
|
||||
__vlAnonCG_nested_cg.cp_container.lo: 8
|
||||
__vlAnonCG_nested_cg.cp_local.hi: 8
|
||||
__vlAnonCG_nested_cg.cp_local.lo: 8
|
||||
__vlAnonCG_nested_cg.cp_static.hi: 8
|
||||
__vlAnonCG_nested_cg.cp_static.lo: 8
|
||||
__vlAnonCG_parameterized_cg.cp_parameterized.hi: 8
|
||||
__vlAnonCG_parameterized_cg.cp_parameterized.lo: 8
|
||||
__vlAnonCG_second_cg.cp_second.hi: 8
|
||||
__vlAnonCG_second_cg.cp_second.lo: 8
|
||||
__vlAnonCG_static_cg.cp_instance.hi: 8
|
||||
__vlAnonCG_static_cg.cp_instance.lo: 8
|
||||
__vlAnonCG_static_cg.cp_static.hi: 8
|
||||
__vlAnonCG_static_cg.cp_static.lo: 8
|
||||
__vlAnonCG_static_only_cg.cp_static_only.hi: 8
|
||||
__vlAnonCG_static_only_cg.cp_static_only.lo: 8
|
||||
__vlAnonCG_this_cg.cp_this.hi: 8
|
||||
__vlAnonCG_this_cg.cp_this.lo: 8
|
||||
__vlAnonCG_this_handle_cg.cp_this_handle.hi: 8
|
||||
__vlAnonCG_this_handle_cg.cp_this_handle.lo: 8
|
||||
__vlAnonCG_this_sample_cg.cp_this_member.hi: 8
|
||||
__vlAnonCG_this_sample_cg.cp_this_member.lo: 8
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_0: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_1: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_10: 16
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_11: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_12: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_13: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_14: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_15: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_2: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_3: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_4: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_5: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_6: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_7: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_8: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_parameter.auto_9: 0
|
||||
__vlAnonCG_this_sample_cg.cp_this_sample.hi: 8
|
||||
__vlAnonCG_this_sample_cg.cp_this_sample.lo: 8
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/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')
|
||||
|
||||
coverage_covergroup_common.run(
|
||||
test,
|
||||
verilator_flags2=[
|
||||
"--dumpi-tree",
|
||||
"9",
|
||||
"--dumpi-tree-json",
|
||||
"9",
|
||||
"--no-json-ids",
|
||||
"--debug-self-test",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,487 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Embedded covergroups whose coverage constructs reference members of the
|
||||
// enclosing class. IEEE 1800-2023 19.4 allows class members in coverpoint
|
||||
// expressions, conditional guards, option initialization, and other coverage
|
||||
// constructs; IEEE 1800-2023 8.11 also allows 'this' within embedded covergroups.
|
||||
|
||||
// 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
|
||||
|
||||
bit [3:0] global_value;
|
||||
|
||||
covergroup GlobalCg;
|
||||
cp_global: coverpoint global_value;
|
||||
endgroup
|
||||
|
||||
class GlobalCgHolder;
|
||||
GlobalCg cg;
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Inner;
|
||||
bit [3:0] value;
|
||||
endclass
|
||||
|
||||
class Transaction;
|
||||
bit [7:0] operand_a;
|
||||
bit [1:0] operand_b;
|
||||
Inner inner;
|
||||
endclass
|
||||
|
||||
class Monitor;
|
||||
bit [3:0] addr; // Direct member of the enclosing class
|
||||
bit enable;
|
||||
Transaction trx; // Class-handle member of the enclosing class
|
||||
|
||||
covergroup mon_cg;
|
||||
cp_addr: coverpoint addr {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_enabled: coverpoint addr iff (enable) {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_op_a: coverpoint trx.operand_a {bins lo = {[0 : 127]}; bins hi = {[128 : 255]};}
|
||||
cp_op_b: coverpoint trx.operand_b;
|
||||
cp_inner: coverpoint trx.inner.value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
addr_x_op_b: cross cp_addr, cp_op_b;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
trx = new;
|
||||
trx.inner = new;
|
||||
mon_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] a, bit [7:0] oa, bit [1:0] ob, bit [3:0] iv);
|
||||
addr = a;
|
||||
enable = a[3];
|
||||
trx.operand_a = oa;
|
||||
trx.operand_b = ob;
|
||||
trx.inner.value = iv;
|
||||
mon_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class BranchMonitor;
|
||||
bit [2:0] value;
|
||||
|
||||
covergroup branch_cg;
|
||||
cp_branch: coverpoint value {bins lo = {[0 : 3]}; bins hi = {[4 : 7]};}
|
||||
endgroup
|
||||
|
||||
function new(bit choose_first);
|
||||
if (choose_first) begin
|
||||
branch_cg = new;
|
||||
end
|
||||
else begin
|
||||
branch_cg = new;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void observe(bit [2:0] v);
|
||||
value = v;
|
||||
branch_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// IEEE 1800-2012 8.13 makes inherited members part of the derived class as if declared
|
||||
// there; 19.4 permits those class members in an embedded covergroup.
|
||||
class BaseMonitor;
|
||||
protected bit [3:0] inherited_value;
|
||||
|
||||
covergroup base_cg;
|
||||
cp_base: coverpoint inherited_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
base_cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class DerivedMonitor extends BaseMonitor;
|
||||
bit [3:0] derived_value;
|
||||
|
||||
covergroup derived_cg;
|
||||
cp_inherited: coverpoint inherited_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_this_inherited: coverpoint this.inherited_value {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
cp_derived: coverpoint derived_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_cross: cross cp_inherited, cp_derived;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
derived_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
inherited_value = v;
|
||||
derived_value = 15 - v;
|
||||
base_cg.sample();
|
||||
derived_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// IEEE 1800-2012 8.18 makes protected members visible to subclasses. Combined with
|
||||
// 8.13 and 19.4, a leaf class covergroup may cover a protected root-class property.
|
||||
class RootMonitor;
|
||||
protected bit [3:0] root_value;
|
||||
endclass
|
||||
|
||||
class MiddleMonitor extends RootMonitor;
|
||||
endclass
|
||||
|
||||
class LeafMonitor extends MiddleMonitor;
|
||||
bit [3:0] leaf_value;
|
||||
|
||||
covergroup leaf_cg;
|
||||
cp_root: coverpoint root_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_leaf: coverpoint leaf_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_cross: cross cp_root, cp_leaf;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
leaf_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
root_value = v;
|
||||
leaf_value = 15 - v;
|
||||
leaf_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ParameterizedBaseMonitor #(
|
||||
int WIDTH = 4
|
||||
);
|
||||
bit [WIDTH-1:0] parameterized_value;
|
||||
endclass
|
||||
|
||||
class ParameterizedDerivedMonitor extends ParameterizedBaseMonitor #(4);
|
||||
covergroup parameterized_cg;
|
||||
cp_parameterized: coverpoint parameterized_value {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
parameterized_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
parameterized_value = v;
|
||||
parameterized_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ThisMonitor;
|
||||
bit [3:0] current;
|
||||
|
||||
covergroup this_cg with function sample(bit [3:0] sampled_current);
|
||||
cp_this: coverpoint current iff (sampled_current == current) {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
this_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
current = v;
|
||||
this_cg.sample(this.current);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ThisSampleMonitor;
|
||||
bit [3:0] sampled_value;
|
||||
localparam bit [3:0] local_value = 4'ha;
|
||||
|
||||
covergroup this_sample_cg with function sample(bit [3:0] sampled_value);
|
||||
cp_this_member: coverpoint this.sampled_value {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
cp_this_parameter: coverpoint this.local_value;
|
||||
cp_this_sample: coverpoint sampled_value iff (sampled_value == this.sampled_value) {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
this_sample_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
this.sampled_value = v;
|
||||
this_sample_cg.sample(this.sampled_value);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
`ifdef VERILATOR
|
||||
// IEEE 1800-2012 8.11 explicitly permits 'this' in covergroups embedded within classes.
|
||||
class ThisHandleMonitor;
|
||||
bit [3:0] current;
|
||||
|
||||
covergroup this_handle_cg;
|
||||
cp_this_handle: coverpoint current iff (this == this) {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
this_handle_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
current = v;
|
||||
this_handle_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
`endif
|
||||
|
||||
class CopyMonitor;
|
||||
bit [3:0] value;
|
||||
|
||||
covergroup copy_cg;
|
||||
cp_copy: coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
copy_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
value = v;
|
||||
copy_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class CloneBaseMonitor;
|
||||
bit [3:0] base_value;
|
||||
|
||||
covergroup cg;
|
||||
cp_base: coverpoint base_value;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class CloneDerivedMonitor extends CloneBaseMonitor;
|
||||
bit [3:0] derived_value;
|
||||
|
||||
covergroup cg;
|
||||
cp_derived: coverpoint derived_value;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class StaticMonitor;
|
||||
static bit [3:0] static_value;
|
||||
bit [3:0] instance_value;
|
||||
|
||||
covergroup static_cg;
|
||||
cp_static: coverpoint static_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_instance: coverpoint instance_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
static_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
static_value = v;
|
||||
instance_value = v;
|
||||
static_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class StaticOnlyMonitor;
|
||||
static bit [3:0] static_value;
|
||||
|
||||
covergroup static_only_cg with function sample(bit [3:0] sampled_value);
|
||||
cp_static_only: coverpoint static_value iff (sampled_value == static_value) {
|
||||
bins lo = {[0 : 7]};
|
||||
bins hi = {[8 : 15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
static_only_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
static_value = v;
|
||||
static_only_cg.sample(this.static_value);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class MultipleMonitor;
|
||||
bit [3:0] first_value;
|
||||
bit [3:0] second_value;
|
||||
|
||||
covergroup first_cg;
|
||||
cp_first: coverpoint first_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
covergroup second_cg;
|
||||
cp_second: coverpoint second_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
first_cg = new;
|
||||
second_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] first, bit [3:0] second);
|
||||
first_value = first;
|
||||
second_value = second;
|
||||
first_cg.sample();
|
||||
second_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class NestedContainer;
|
||||
protected static bit [3:0] static_value;
|
||||
bit [3:0] value;
|
||||
|
||||
class NestedMonitor;
|
||||
bit [3:0] local_value;
|
||||
NestedContainer container;
|
||||
|
||||
covergroup nested_cg;
|
||||
cp_local: coverpoint local_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
cp_container: coverpoint container.value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
// IEEE 1800-2012 8.23 permits implicit access to static outer-class members.
|
||||
cp_static: coverpoint static_value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
function new(NestedContainer container_arg);
|
||||
container = container_arg;
|
||||
nested_cg = new;
|
||||
endfunction
|
||||
|
||||
function void observe(bit [3:0] v);
|
||||
local_value = v;
|
||||
container.value = 15 - v;
|
||||
static_value = v;
|
||||
nested_cg.sample();
|
||||
endfunction
|
||||
endclass
|
||||
endclass
|
||||
|
||||
class UnconstructedMonitor;
|
||||
bit [3:0] local_value;
|
||||
|
||||
covergroup unconstructed_cg;
|
||||
cp: coverpoint local_value;
|
||||
cp2: coverpoint local_value[2:0];
|
||||
cp_x_cp2: cross cp, cp2;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Monitor mon;
|
||||
BranchMonitor branch_a;
|
||||
BranchMonitor branch_b;
|
||||
DerivedMonitor derived;
|
||||
LeafMonitor leaf;
|
||||
ParameterizedDerivedMonitor parameterized;
|
||||
ThisMonitor this_mon;
|
||||
ThisSampleMonitor this_sample_mon;
|
||||
`ifdef VERILATOR
|
||||
ThisHandleMonitor this_handle_mon;
|
||||
`endif
|
||||
CopyMonitor copy_src;
|
||||
CopyMonitor copy_dst;
|
||||
GlobalCgHolder global_src;
|
||||
GlobalCgHolder global_dst;
|
||||
CloneDerivedMonitor clone_src;
|
||||
CloneDerivedMonitor clone_dst;
|
||||
CloneBaseMonitor clone_base_view;
|
||||
StaticMonitor static_mon;
|
||||
StaticOnlyMonitor static_only_mon;
|
||||
MultipleMonitor multiple_mon;
|
||||
NestedContainer nested_container;
|
||||
NestedContainer::NestedMonitor nested_mon;
|
||||
UnconstructedMonitor unconstructed_mon;
|
||||
int i;
|
||||
|
||||
initial begin
|
||||
mon = new;
|
||||
branch_a = new(1);
|
||||
branch_b = new(0);
|
||||
derived = new;
|
||||
leaf = new;
|
||||
parameterized = new;
|
||||
this_mon = new;
|
||||
this_sample_mon = new;
|
||||
`ifdef VERILATOR
|
||||
this_handle_mon = new;
|
||||
`endif
|
||||
copy_src = new;
|
||||
global_src = new;
|
||||
clone_src = new;
|
||||
static_mon = new;
|
||||
static_only_mon = new;
|
||||
multiple_mon = new;
|
||||
nested_container = new;
|
||||
nested_mon = new(nested_container);
|
||||
unconstructed_mon = new;
|
||||
`checkd(unconstructed_mon.unconstructed_cg == null, 1);
|
||||
|
||||
for (i = 0; i < 16; ++i) begin
|
||||
mon.observe(i[3:0], i[7:0] * 17, i[1:0], i[3:0]);
|
||||
derived.observe(i[3:0]);
|
||||
leaf.observe(i[3:0]);
|
||||
parameterized.observe(i[3:0]);
|
||||
this_mon.observe(i[3:0]);
|
||||
this_sample_mon.observe(i[3:0]);
|
||||
`ifdef VERILATOR
|
||||
this_handle_mon.observe(i[3:0]);
|
||||
`endif
|
||||
static_mon.observe(i[3:0]);
|
||||
static_only_mon.observe(i[3:0]);
|
||||
multiple_mon.observe(i[3:0], 15 - i[3:0]);
|
||||
nested_mon.observe(i[3:0]);
|
||||
end
|
||||
|
||||
for (i = 0; i < 8; ++i) begin
|
||||
branch_a.observe(i[2:0]);
|
||||
branch_b.observe(i[2:0]);
|
||||
end
|
||||
|
||||
copy_src.observe(4'h1);
|
||||
copy_dst = new copy_src;
|
||||
`checkd(copy_dst.copy_cg == null, 1);
|
||||
global_dst = new global_src;
|
||||
`checkd(global_dst.cg == global_src.cg, 1);
|
||||
clone_dst = new clone_src;
|
||||
clone_base_view = clone_dst;
|
||||
`checkd(clone_dst.cg == null, 1);
|
||||
`checkd(clone_base_view.cg == null, 1);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
%Error-ENCAPSULATED: t/t_covergroup_embedded_nested_bad.v:42:20: 'local_value' is hidden as 'local' within this context (IEEE 1800-2023 8.18)
|
||||
: ... note: In instance 't'
|
||||
42 | cp: coverpoint local_value;
|
||||
| ^~~~~~~~~~~
|
||||
t/t_covergroup_embedded_nested_bad.v:42:20: ... Location of definition
|
||||
37 | local bit [3:0] local_value;
|
||||
| ^~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/ENCAPSULATED?v=latest
|
||||
%Error: t/t_covergroup_embedded_nested_bad.v:13:22: Non-static member 'outer_value' of an outer class requires an explicit object handle (IEEE 1800-2023 8.23).
|
||||
: ... note: In instance 't'
|
||||
13 | cp: coverpoint outer_value;
|
||||
| ^~~~~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_covergroup_embedded_nested_bad.v:25:39: Non-static member 'outer_value' of an outer class requires an explicit object handle (IEEE 1800-2023 8.23).
|
||||
: ... note: In instance 't'
|
||||
25 | cp: coverpoint inner_value iff (outer_value != 0);
|
||||
| ^~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -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
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Outer;
|
||||
bit [3:0] outer_value;
|
||||
|
||||
class CoverpointInner;
|
||||
covergroup cg;
|
||||
cp: coverpoint outer_value;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class IffInner;
|
||||
bit [3:0] inner_value;
|
||||
|
||||
covergroup cg;
|
||||
cp: coverpoint inner_value iff (outer_value != 0);
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
endclass
|
||||
|
||||
// IEEE 1800-2012 8.18 does not make local base-class members visible to subclasses,
|
||||
// even though 8.13 makes non-local inherited members part of the derived class.
|
||||
class LocalBase;
|
||||
local bit [3:0] local_value;
|
||||
endclass
|
||||
|
||||
class LocalDerived extends LocalBase;
|
||||
covergroup cg;
|
||||
cp: coverpoint local_value;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Outer::CoverpointInner coverpoint_inner;
|
||||
Outer::IffInner iff_inner;
|
||||
LocalDerived local_derived;
|
||||
|
||||
initial begin
|
||||
coverpoint_inner = new;
|
||||
iff_inner = new;
|
||||
local_derived = new;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:23:34: Unsupported: 'covergroup' coverpoint referencing enclosing class member; ignoring covergroup '__vlAnonCG_cov_trans'
|
||||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:44:23: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_param'
|
||||
: ... note: In instance 't'
|
||||
23 | trans_start_addr: coverpoint trans_collected.addr {option.auto_bin_max = 16;}
|
||||
| ^~~~~~~~~~~~~~~
|
||||
44 | cp: coverpoint st.test;
|
||||
| ^~~~
|
||||
... 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_embedded_unsup.v:46:23: Unsupported: 'covergroup' coverpoint dereferencing a class handle member (parameterized covergroup); ignoring covergroup '__vlAnonCG_cov_param'
|
||||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:60:37: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_mixed'
|
||||
: ... note: In instance 't'
|
||||
46 | cp: coverpoint st.test;
|
||||
| ^~~~
|
||||
60 | cp: coverpoint local_value + st.test;
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test that two currently-unsupported coverpoint reference styles are properly flagged
|
||||
// as COVIGN: references to containing-class members ; references to covergroup formal
|
||||
// parameters
|
||||
// Test that unsupported coverpoint reference styles are properly flagged as COVERIGN.
|
||||
|
||||
class ubus_transfer;
|
||||
bit [15:0] addr;
|
||||
|
|
@ -53,12 +51,29 @@ class parameterized_monitor;
|
|||
endfunction
|
||||
endclass
|
||||
|
||||
class mixed_monitor;
|
||||
bit [3:0] local_value;
|
||||
coverage_state cs;
|
||||
|
||||
// The formal-handle guard must still apply when the expression also has an enclosing member.
|
||||
covergroup cov_mixed(coverage_state st);
|
||||
cp: coverpoint local_value + st.test;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
cs = new;
|
||||
cov_mixed = new(cs);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
ubus_master_monitor m;
|
||||
parameterized_monitor p;
|
||||
mixed_monitor q;
|
||||
initial begin
|
||||
m = new;
|
||||
p = new;
|
||||
q = new;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue