Support embedded covergroup clocking events (#8028)
This commit is contained in:
parent
dde6aa34ce
commit
645b8cdf24
|
|
@ -1568,15 +1568,23 @@ class AstFork final : public AstNodeBlock {
|
||||||
//
|
//
|
||||||
// @astgen op3 := forksp : List[AstBegin]
|
// @astgen op3 := forksp : List[AstBegin]
|
||||||
const VJoinType m_joinType; // Join keyword type
|
const VJoinType m_joinType; // Join keyword type
|
||||||
|
bool m_immediateStart = false; // Fork starts before its parent blocks or exits
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
||||||
: ASTGEN_SUPER_Fork(fl, name)
|
: ASTGEN_SUPER_Fork(fl, name)
|
||||||
, m_joinType{joinType} {}
|
, m_joinType{joinType} {}
|
||||||
ASTGEN_MEMBERS_AstFork;
|
ASTGEN_MEMBERS_AstFork;
|
||||||
|
bool sameNode(const AstNode* samep) const override {
|
||||||
|
const AstFork* const asamep = VN_DBG_AS(samep, Fork);
|
||||||
|
return joinType() == asamep->joinType() && immediateStart() == asamep->immediateStart();
|
||||||
|
}
|
||||||
bool isTimingControl() const override { return !joinType().joinNone(); }
|
bool isTimingControl() const override { return !joinType().joinNone(); }
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
VJoinType joinType() const { return m_joinType; }
|
VJoinType joinType() const { return m_joinType; }
|
||||||
|
bool immediateStart() const { return m_immediateStart; }
|
||||||
|
void immediateStart(bool flag) { m_immediateStart = flag; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// === AstNodeCoverOrAssert ===
|
// === AstNodeCoverOrAssert ===
|
||||||
|
|
|
||||||
|
|
@ -3649,9 +3649,11 @@ void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||||
void AstFork::dump(std::ostream& str) const {
|
void AstFork::dump(std::ostream& str) const {
|
||||||
this->AstNodeBlock::dump(str);
|
this->AstNodeBlock::dump(str);
|
||||||
str << " [" << joinType() << "]";
|
str << " [" << joinType() << "]";
|
||||||
|
if (immediateStart()) str << " [IMMEDIATE]";
|
||||||
}
|
}
|
||||||
void AstFork::dumpJson(std::ostream& str) const {
|
void AstFork::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonStr(str, "joinType", joinType().ascii());
|
dumpJsonStr(str, "joinType", joinType().ascii());
|
||||||
|
dumpJsonBoolFuncIf(str, immediateStart);
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
void AstStop::dump(std::ostream& str) const {
|
void AstStop::dump(std::ostream& str) const {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstClass* m_covergroupp = nullptr; // Current covergroup being processed
|
AstClass* m_covergroupp = nullptr; // Current covergroup being processed
|
||||||
AstClass* m_enclosingClassp = nullptr; // Class lexically enclosing the covergroup (if any)
|
AstClass* m_enclosingClassp = nullptr; // Class lexically enclosing the covergroup, if any
|
||||||
|
AstVar* m_embeddedVarp = nullptr; // Embedded covergroup member of m_enclosingClassp, if any
|
||||||
AstFunc* m_sampleFuncp = nullptr; // Current sample() function
|
AstFunc* m_sampleFuncp = nullptr; // Current sample() function
|
||||||
AstFunc* m_constructorp = nullptr; // Current constructor
|
AstFunc* m_constructorp = nullptr; // Current constructor
|
||||||
std::vector<AstCoverpoint*> m_coverpoints; // Coverpoints in current covergroup
|
std::vector<AstCoverpoint*> m_coverpoints; // Coverpoints in current covergroup
|
||||||
|
|
@ -71,6 +72,21 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
};
|
};
|
||||||
std::vector<BinInfo> m_binInfos; // All bins in current covergroup
|
std::vector<BinInfo> m_binInfos; // All bins in current covergroup
|
||||||
|
|
||||||
|
struct EmbeddedEventTrigger final {
|
||||||
|
FileLine* eventFl; // Clocking-event source location
|
||||||
|
AstVar* baseVarp; // Base enclosing-class member in the event expression
|
||||||
|
AstVar* memberVarp; // Selected member in a 'base.member' expression, or nullptr
|
||||||
|
VEdgeType edgeType; // Clocking-event edge qualifier
|
||||||
|
AstVar* prevVarp; // Member containing the previous event value, or nullptr
|
||||||
|
EmbeddedEventTrigger(FileLine* eventFl, AstVar* baseVarp, AstVar* memberVarp,
|
||||||
|
VEdgeType edgeType)
|
||||||
|
: eventFl{eventFl}
|
||||||
|
, baseVarp{baseVarp}
|
||||||
|
, memberVarp{memberVarp}
|
||||||
|
, edgeType{edgeType}
|
||||||
|
, prevVarp{nullptr} {}
|
||||||
|
};
|
||||||
|
|
||||||
std::set<std::string> m_crossedCpNames; // Coverpoints referenced by a cross (kept legacy)
|
std::set<std::string> m_crossedCpNames; // Coverpoints referenced by a cross (kept legacy)
|
||||||
std::vector<AstVar*> m_convCpVars; // VlCoverpoint members of converted coverpoints
|
std::vector<AstVar*> m_convCpVars; // VlCoverpoint members of converted coverpoints
|
||||||
AstCDType* m_vlCoverpointDTypep = nullptr; // Shared "VlCoverpoint" C++ member type
|
AstCDType* m_vlCoverpointDTypep = nullptr; // Shared "VlCoverpoint" C++ member type
|
||||||
|
|
@ -1718,6 +1734,220 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEmbeddedCovergroupVar(const AstVar* varp) const {
|
||||||
|
if (!varp || !varp->isClassMember()) return false;
|
||||||
|
const AstClassRefDType* const refp = VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType);
|
||||||
|
return refp && refp->classp() == m_covergroupp;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstVar* findEmbeddedCovergroupVar() const {
|
||||||
|
if (!m_enclosingClassp) return nullptr;
|
||||||
|
for (AstNode* itemp = m_enclosingClassp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||||
|
if (AstVar* const varp = VN_CAST(itemp, Var)) {
|
||||||
|
if (isEmbeddedCovergroupVar(varp)) return varp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// V3LinkParse always creates an implicit variable for an embedded covergroup.
|
||||||
|
return nullptr; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AstNodeAssign*> findCovergroupConstructions() {
|
||||||
|
std::vector<AstNodeAssign*> foundps;
|
||||||
|
if (!m_embeddedVarp) return foundps;
|
||||||
|
AstFunc* const enclosingNewp
|
||||||
|
= VN_CAST(m_memberMap.findMember(m_enclosingClassp, "new"), Func);
|
||||||
|
if (!enclosingNewp) return foundps;
|
||||||
|
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() != m_embeddedVarp) return;
|
||||||
|
const AstClassRefDType* const refp = VN_CAST(newp->dtypep(), ClassRefDType);
|
||||||
|
if (refp && refp->classp() == m_covergroupp) foundps.push_back(asgnp);
|
||||||
|
});
|
||||||
|
return foundps;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeAssign* findInvalidEmbeddedCovergroupAssignment() {
|
||||||
|
if (!m_embeddedVarp) return nullptr;
|
||||||
|
std::set<const AstNodeAssign*> constructorAssignps;
|
||||||
|
AstFunc* const enclosingNewp
|
||||||
|
= VN_CAST(m_memberMap.findMember(m_enclosingClassp, "new"), Func);
|
||||||
|
if (enclosingNewp) {
|
||||||
|
enclosingNewp->foreach([&](AstNodeAssign* asgnp) {
|
||||||
|
const AstVarRef* const refp = VN_CAST(asgnp->lhsp(), VarRef);
|
||||||
|
if (refp && refp->varp() == m_embeddedVarp) constructorAssignps.insert(asgnp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
AstNodeAssign* invalidp = nullptr;
|
||||||
|
m_enclosingClassp->foreach([&](AstNodeAssign* asgnp) {
|
||||||
|
if (invalidp || constructorAssignps.count(asgnp)) return;
|
||||||
|
const AstVarRef* const refp = VN_CAST(asgnp->lhsp(), VarRef);
|
||||||
|
if (refp && refp->varp() == m_embeddedVarp) invalidp = asgnp;
|
||||||
|
});
|
||||||
|
return invalidp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<const AstVar*> enclosingInstanceVars() const {
|
||||||
|
std::set<const AstVar*> vars;
|
||||||
|
if (m_enclosingClassp) {
|
||||||
|
m_enclosingClassp->foreachMember([&](AstClass* const, AstVar* const varp) {
|
||||||
|
if (isEnclosingInstanceVar(varp)) vars.insert(varp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasEnclosingEventRef(AstCovergroup* cgp) const {
|
||||||
|
if (!m_embeddedVarp || !cgp->eventp()) return false;
|
||||||
|
const std::set<const AstVar*> enclosingVars = enclosingInstanceVars();
|
||||||
|
bool found = false;
|
||||||
|
cgp->eventp()->foreach([&](AstVarRef* refp) {
|
||||||
|
if (enclosingVars.count(refp->varp())) found = true;
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseEmbeddedEventExpr(AstNodeExpr* exprp, AstVar*& baseVarp,
|
||||||
|
AstVar*& memberVarp) {
|
||||||
|
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||||
|
baseVarp = refp->varp();
|
||||||
|
memberVarp = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AstMemberSel* const selp = VN_CAST(exprp, MemberSel);
|
||||||
|
if (!selp) return false;
|
||||||
|
AstVarRef* const baseRefp = VN_CAST(selp->fromp(), VarRef);
|
||||||
|
if (!baseRefp) return false;
|
||||||
|
baseVarp = baseRefp->varp();
|
||||||
|
memberVarp = selp->varp();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEventLvalue(AstNodeExpr* exprp, const EmbeddedEventTrigger& trigger) const {
|
||||||
|
if (AstSel* const selp = VN_CAST(exprp, Sel)) exprp = selp->fromp();
|
||||||
|
AstVar* baseVarp = nullptr;
|
||||||
|
AstVar* memberVarp = nullptr;
|
||||||
|
if (!parseEmbeddedEventExpr(exprp, baseVarp, memberVarp)) return false;
|
||||||
|
return baseVarp == trigger.baseVarp && memberVarp == trigger.memberVarp;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeExpr* newEventRead(FileLine* fl, const EmbeddedEventTrigger& trigger) const {
|
||||||
|
AstNodeExpr* const basep = new AstVarRef{fl, trigger.baseVarp, VAccess::READ};
|
||||||
|
if (!trigger.memberVarp) return basep;
|
||||||
|
AstMemberSel* const selp = new AstMemberSel{fl, basep, trigger.memberVarp};
|
||||||
|
selp->access(VAccess::READ);
|
||||||
|
return selp;
|
||||||
|
}
|
||||||
|
|
||||||
|
string eventPrevName(const EmbeddedEventTrigger& trigger, size_t triggerIndex) const {
|
||||||
|
string name = "__Vcg_prev_" + m_embeddedVarp->name() + "_" + std::to_string(triggerIndex)
|
||||||
|
+ "_" + trigger.baseVarp->name();
|
||||||
|
if (trigger.memberVarp) name += "_" + trigger.memberVarp->name();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeExpr* newEmbeddedVarNonNull(FileLine* fl) const {
|
||||||
|
return new AstNeq{fl, new AstVarRef{fl, m_embeddedVarp, VAccess::READ},
|
||||||
|
new AstConst{fl, AstConst::Null{}}};
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeStmt* newSampleStmt(FileLine* fl) const {
|
||||||
|
AstMethodCall* const callp = new AstMethodCall{
|
||||||
|
fl, new AstVarRef{fl, m_embeddedVarp, VAccess::READ}, "sample", nullptr};
|
||||||
|
callp->taskp(m_sampleFuncp);
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
return callp->makeStmt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void installEmbeddedEventFork(AstSenTree* eventp,
|
||||||
|
const std::vector<AstNodeAssign*>& constructps) {
|
||||||
|
// IEEE 1800-2023 19.3 samples coverpoints whenever their clocking event occurs. A
|
||||||
|
// per-instance event cannot use V3Active's static sensitivity path, so spawn
|
||||||
|
// 'fork forever begin @(event); cg.sample(); end join_none' after each construction.
|
||||||
|
for (AstNodeAssign* const constructp : constructps) {
|
||||||
|
FileLine* const fl = constructp->fileline();
|
||||||
|
AstLoop* const loopp = new AstLoop{fl};
|
||||||
|
loopp->addStmtsp(new AstEventControl{fl, eventp->cloneTree(false), nullptr});
|
||||||
|
loopp->addStmtsp(new AstIf{fl, newEmbeddedVarNonNull(fl), newSampleStmt(fl)});
|
||||||
|
AstFork* const forkp = new AstFork{fl, VJoinType::JOIN_NONE};
|
||||||
|
forkp->immediateStart(true);
|
||||||
|
forkp->addForksp(new AstBegin{fl, "", loopp, true});
|
||||||
|
constructp->addNextHere(forkp);
|
||||||
|
}
|
||||||
|
VL_DO_DANGLING(pushDeletep(eventp), eventp);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeExpr* newEventReadyCondition(FileLine* fl, const EmbeddedEventTrigger& trigger) const {
|
||||||
|
AstNodeExpr* const curp = newEventRead(fl, trigger);
|
||||||
|
AstNodeExpr* const prevp = new AstVarRef{fl, trigger.prevVarp, VAccess::READ};
|
||||||
|
AstNodeExpr* edgep = nullptr;
|
||||||
|
// IEEE 1800-2023 9.4.2 detects edge-qualified events only on the expression's LSB,
|
||||||
|
// while an implicit change event observes the complete expression.
|
||||||
|
if (trigger.edgeType == VEdgeType::ET_POSEDGE) {
|
||||||
|
edgep = new AstSel{fl, new AstAnd{fl, curp, new AstNot{fl, prevp}}, 0, 1};
|
||||||
|
} else if (trigger.edgeType == VEdgeType::ET_NEGEDGE) {
|
||||||
|
edgep = new AstSel{fl, new AstAnd{fl, new AstNot{fl, curp}, prevp}, 0, 1};
|
||||||
|
} else if (trigger.edgeType == VEdgeType::ET_BOTHEDGE) {
|
||||||
|
edgep = new AstSel{fl, new AstXor{fl, curp, prevp}, 0, 1};
|
||||||
|
} else {
|
||||||
|
edgep = new AstNeq{fl, curp, prevp};
|
||||||
|
}
|
||||||
|
return new AstLogAnd{fl, newEmbeddedVarNonNull(fl), edgep};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EmbeddedEventTrigger> collectEmbeddedEventTriggers(AstCovergroup* cgp) {
|
||||||
|
std::vector<EmbeddedEventTrigger> triggers;
|
||||||
|
const std::set<const AstVar*> enclosingVars = enclosingInstanceVars();
|
||||||
|
for (AstNode* senp = cgp->eventp()->sensesp(); senp; senp = senp->nextp()) {
|
||||||
|
AstSenItem* const itemp = VN_AS(senp, SenItem);
|
||||||
|
AstVar* baseVarp = nullptr;
|
||||||
|
AstVar* memberVarp = nullptr;
|
||||||
|
if (!parseEmbeddedEventExpr(itemp->sensp(), baseVarp, memberVarp)
|
||||||
|
|| !enclosingVars.count(baseVarp)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
triggers.emplace_back(itemp->fileline(), baseVarp, memberVarp, itemp->edgeType());
|
||||||
|
}
|
||||||
|
return triggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void installEmbeddedEventTriggers(std::vector<EmbeddedEventTrigger>& triggers,
|
||||||
|
const std::vector<AstNodeAssign*>& constructps) {
|
||||||
|
// Without --timing, approximate a per-instance event by sampling after assignments
|
||||||
|
// within the enclosing class. External writes and exact scheduling cannot be observed.
|
||||||
|
if (constructps.empty()) return;
|
||||||
|
for (size_t triggerIndex = 0; triggerIndex < triggers.size(); ++triggerIndex) {
|
||||||
|
EmbeddedEventTrigger& trigger = triggers[triggerIndex];
|
||||||
|
std::vector<AstNodeAssign*> assignps;
|
||||||
|
m_enclosingClassp->foreach([&](AstNodeAssign* asgnp) {
|
||||||
|
if (isEventLvalue(asgnp->lhsp(), trigger)) assignps.push_back(asgnp);
|
||||||
|
});
|
||||||
|
if (assignps.empty()) {
|
||||||
|
trigger.eventFl->v3warn(
|
||||||
|
COVERIGN, "Unsupported: 'covergroup' clocking event signal has no assignment "
|
||||||
|
"within the enclosing class; no coverage sampled. Use --timing for "
|
||||||
|
"full support.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AstNodeDType* const dtypep
|
||||||
|
= trigger.memberVarp ? trigger.memberVarp->dtypep() : trigger.baseVarp->dtypep();
|
||||||
|
AstVar* const prevVarp = new AstVar{trigger.eventFl, VVarType::MEMBER,
|
||||||
|
eventPrevName(trigger, triggerIndex), dtypep};
|
||||||
|
prevVarp->isStatic(false);
|
||||||
|
m_enclosingClassp->addMembersp(prevVarp);
|
||||||
|
trigger.prevVarp = prevVarp;
|
||||||
|
for (AstNodeAssign* const asgnp : assignps) {
|
||||||
|
FileLine* const fl = asgnp->fileline();
|
||||||
|
AstIf* const ifp
|
||||||
|
= new AstIf{fl, newEventReadyCondition(fl, trigger), newSampleStmt(fl)};
|
||||||
|
ifp->addNextHere(new AstAssign{fl,
|
||||||
|
new AstVarRef{fl, trigger.prevVarp, VAccess::WRITE},
|
||||||
|
newEventRead(fl, trigger)});
|
||||||
|
asgnp->addNextHere(ifp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void deleteCoverageItems() {
|
void deleteCoverageItems() {
|
||||||
for (AstCoverpoint* const cpp : m_coverpoints) {
|
for (AstCoverpoint* const cpp : m_coverpoints) {
|
||||||
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp);
|
||||||
|
|
@ -1771,7 +2001,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
return visitor.offenderp();
|
return visitor.offenderp();
|
||||||
}
|
}
|
||||||
|
|
||||||
AstVarRef* installEnclosingBackPointer() {
|
AstVarRef* installEnclosingBackPointer(const std::vector<AstNodeAssign*>& constructps) {
|
||||||
// Simple-case support for embedded covergroups (IEEE 1800-2023 19.4) whose
|
// Simple-case support for embedded covergroups (IEEE 1800-2023 19.4) whose
|
||||||
// coverpoints reference members of the enclosing class ("Class members can be used
|
// coverpoints reference members of the enclosing class ("Class members can be used
|
||||||
// in coverpoint expressions"). The covergroup is lowered into a sibling class with
|
// in coverpoint expressions"). The covergroup is lowered into a sibling class with
|
||||||
|
|
@ -1789,11 +2019,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
for (AstNode* itemp = m_covergroupp->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);
|
if (const AstVar* const varp = VN_CAST(itemp, Var)) ownVars.insert(varp);
|
||||||
}
|
}
|
||||||
std::set<const AstVar*> enclosingVars;
|
const std::set<const AstVar*> enclosingVars = enclosingInstanceVars();
|
||||||
m_enclosingClassp->foreachMember([&](AstClass* const, AstVar* const varp) {
|
|
||||||
if (isEnclosingInstanceVar(varp)) enclosingVars.insert(varp);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::vector<AstVarRef*> refsToRewrite;
|
std::vector<AstVarRef*> refsToRewrite;
|
||||||
std::vector<AstThisRef*> thisRefsToRewrite;
|
std::vector<AstThisRef*> thisRefsToRewrite;
|
||||||
const auto scan = [&](AstNode* rootp) {
|
const auto scan = [&](AstNode* rootp) {
|
||||||
|
|
@ -1822,32 +2048,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
for (AstCoverCross* const crossp : m_coverCrosses) scan(crossp);
|
for (AstCoverCross* const crossp : m_coverCrosses) scan(crossp);
|
||||||
if (invalidp || !offenderp) return invalidp;
|
if (invalidp || !offenderp) return invalidp;
|
||||||
|
|
||||||
AstVar* embeddedVarp = nullptr;
|
UASSERT_OBJ(m_embeddedVarp, m_covergroupp, "Embedded covergroup variable not found");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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.
|
// Commit: add the back-pointer member, rewrite the references, initialize the handle.
|
||||||
FileLine* const fl = m_covergroupp->fileline();
|
FileLine* const fl = m_covergroupp->fileline();
|
||||||
AstClassRefDType* const enclDTypep = new AstClassRefDType{fl, m_enclosingClassp, nullptr};
|
AstClassRefDType* const enclDTypep = new AstClassRefDType{fl, m_enclosingClassp, nullptr};
|
||||||
|
|
@ -1880,14 +2081,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
UINFO(9, "Visiting class: " << nodep->name() << " isCovergroup=" << nodep->isCovergroup());
|
UINFO(9, "Visiting class: " << nodep->name() << " isCovergroup=" << nodep->isCovergroup());
|
||||||
if (nodep->isCovergroup()) {
|
if (nodep->isCovergroup()) {
|
||||||
VL_RESTORER(m_covergroupp);
|
VL_RESTORER(m_covergroupp);
|
||||||
|
VL_RESTORER(m_embeddedVarp);
|
||||||
VL_RESTORER(m_sampleFuncp);
|
VL_RESTORER(m_sampleFuncp);
|
||||||
VL_RESTORER(m_constructorp);
|
VL_RESTORER(m_constructorp);
|
||||||
VL_RESTORER_CLEAR(m_coverpoints);
|
VL_RESTORER_CLEAR(m_coverpoints);
|
||||||
VL_RESTORER_CLEAR(m_coverpointMap);
|
VL_RESTORER_CLEAR(m_coverpointMap);
|
||||||
VL_RESTORER_CLEAR(m_coverCrosses);
|
VL_RESTORER_CLEAR(m_coverCrosses);
|
||||||
m_covergroupp = nodep;
|
m_covergroupp = nodep;
|
||||||
|
m_embeddedVarp = findEmbeddedCovergroupVar();
|
||||||
m_sampleFuncp = nullptr;
|
m_sampleFuncp = nullptr;
|
||||||
m_constructorp = nullptr;
|
m_constructorp = nullptr;
|
||||||
|
std::vector<EmbeddedEventTrigger> embeddedEventTriggers;
|
||||||
|
AstSenTree* embeddedEventForkp = nullptr;
|
||||||
|
|
||||||
// Extract and store the clocking event from AstCovergroup node
|
// Extract and store the clocking event from AstCovergroup node
|
||||||
// The parser creates this node to preserve the event information
|
// The parser creates this node to preserve the event information
|
||||||
|
|
@ -1900,33 +2105,42 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
// event exists, so cgp->eventp() is always non-null here.
|
// event exists, so cgp->eventp() is always non-null here.
|
||||||
UASSERT_OBJ(cgp->eventp(), cgp,
|
UASSERT_OBJ(cgp->eventp(), cgp,
|
||||||
"Sentinel AstCovergroup in class must have non-null eventp");
|
"Sentinel AstCovergroup in class must have non-null eventp");
|
||||||
// Check if the clocking event references a member variable (unsupported)
|
if (hasEnclosingEventRef(cgp)) {
|
||||||
// Clocking events should be on signals/nets, not class members
|
UASSERT_OBJ(m_embeddedVarp, cgp,
|
||||||
bool eventUnsupported = false;
|
"Embedded covergroup event has no instance variable");
|
||||||
for (AstNode* senp = cgp->eventp()->sensesp(); senp; senp = senp->nextp()) {
|
// IEEE 1800-2023 19.4 permits assignment to an embedded covergroup
|
||||||
AstSenItem* const senItemp = VN_AS(senp, SenItem);
|
// variable only in the enclosing class's new method.
|
||||||
if (AstVarRef* const varrefp // LCOV_EXCL_BR_LINE
|
if (AstNodeAssign* const invalidp
|
||||||
= VN_CAST(senItemp->sensp(), VarRef)) {
|
= findInvalidEmbeddedCovergroupAssignment()) {
|
||||||
if (varrefp->varp()->isClassMember()) {
|
invalidp->v3error(
|
||||||
cgp->v3warn(COVERIGN, "Unsupported: 'covergroup' clocking event "
|
"Embedded covergroup variable "
|
||||||
"on member variable");
|
<< m_embeddedVarp->prettyNameQ()
|
||||||
eventUnsupported = true;
|
<< " may only be assigned in the enclosing class's 'new' method "
|
||||||
|
"(IEEE 1800-2023 19.4).");
|
||||||
|
hasUnsupportedEvent = true;
|
||||||
|
VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp);
|
||||||
|
itemp = nextp;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (v3Global.opt.timing().isSetTrue()) {
|
||||||
|
embeddedEventForkp = cgp->eventp()->unlinkFrBack();
|
||||||
|
} else {
|
||||||
|
embeddedEventTriggers = collectEmbeddedEventTriggers(cgp);
|
||||||
|
if (embeddedEventTriggers.empty()) {
|
||||||
|
cgp->v3warn(COVERIGN,
|
||||||
|
"Unsupported: 'covergroup' clocking event on complex "
|
||||||
|
"member expression; use --timing for full support.");
|
||||||
hasUnsupportedEvent = true;
|
hasUnsupportedEvent = true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp);
|
||||||
|
|
||||||
if (!eventUnsupported) {
|
|
||||||
// Leave cgp in the class membersp so the SenTree stays
|
|
||||||
// linked in the AST. V3Active will find it via membersp,
|
|
||||||
// use the event, then delete the AstCovergroup itself.
|
|
||||||
UINFO(4, "Keeping covergroup event node for V3Active: " << nodep->name());
|
|
||||||
itemp = nextp;
|
itemp = nextp;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Remove the AstCovergroup node - either unsupported event or no event
|
// V3Active handles events that do not depend on an enclosing instance.
|
||||||
VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp);
|
UINFO(4, "Keeping covergroup event node for V3Active: " << nodep->name());
|
||||||
|
itemp = nextp;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
itemp = nextp;
|
itemp = nextp;
|
||||||
}
|
}
|
||||||
|
|
@ -1956,21 +2170,31 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
"class handle member; ignoring covergroup "
|
"class handle member; ignoring covergroup "
|
||||||
<< nodep->prettyNameQ());
|
<< nodep->prettyNameQ());
|
||||||
deleteCoverageItems();
|
deleteCoverageItems();
|
||||||
|
if (embeddedEventForkp) {
|
||||||
|
VL_DO_DANGLING(pushDeletep(embeddedEventForkp), embeddedEventForkp);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<AstNodeAssign*> constructps = findCovergroupConstructions();
|
||||||
|
|
||||||
// Embedded covergroups (IEEE 1800-2023 19.4): coverpoints, iff expressions, and
|
// Embedded covergroups (IEEE 1800-2023 19.4): coverpoints, iff expressions, and
|
||||||
// crosses may reference members of the enclosing class. The covergroup is lowered
|
// 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
|
// into a sibling class with no implicit handle to the enclosing instance. Install
|
||||||
// an explicit back-pointer and route the references through it.
|
// an explicit back-pointer and route the references through it.
|
||||||
if (AstVarRef* const invalidp = installEnclosingBackPointer()) {
|
if (AstVarRef* const invalidp = installEnclosingBackPointer(constructps)) {
|
||||||
invalidp->v3error("Non-static member "
|
invalidp->v3error("Non-static member "
|
||||||
<< invalidp->varp()->prettyNameQ()
|
<< invalidp->varp()->prettyNameQ()
|
||||||
<< " of an outer class requires an explicit "
|
<< " of an outer class requires an explicit "
|
||||||
"object handle (IEEE 1800-2023 8.23).");
|
"object handle (IEEE 1800-2023 8.23).");
|
||||||
deleteCoverageItems();
|
deleteCoverageItems();
|
||||||
|
if (embeddedEventForkp) {
|
||||||
|
VL_DO_DANGLING(pushDeletep(embeddedEventForkp), embeddedEventForkp);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
installEmbeddedEventTriggers(embeddedEventTriggers, constructps);
|
||||||
|
if (embeddedEventForkp) installEmbeddedEventFork(embeddedEventForkp, constructps);
|
||||||
processCovergroup();
|
processCovergroup();
|
||||||
// Remove lowered coverpoints/crosses from the class - they have been
|
// Remove lowered coverpoints/crosses from the class - they have been
|
||||||
// fully translated into C++ code and must not reach downstream passes
|
// fully translated into C++ code and must not reach downstream passes
|
||||||
|
|
|
||||||
|
|
@ -658,8 +658,10 @@ class ForkVisitor final : public VNVisitor {
|
||||||
// join_any block the parent process, deferring branch start with a synthetic #0 delay is
|
// join_any block the parent process, deferring branch start with a synthetic #0 delay is
|
||||||
// normally only needed for join_none. A fork that can be disabled by name needs the same
|
// normally only needed for join_none. A fork that can be disabled by name needs the same
|
||||||
// deferral for every join type so all branches register their processes before any branch
|
// deferral for every join type so all branches register their processes before any branch
|
||||||
// body can disable the block.
|
// body can disable the block. Compiler-generated immediate-start forks already have the
|
||||||
if (nodep->joinType().joinNone() || forkIsDisableable(nodep)) {
|
// required ordering and must arm their event controls before the parent continues.
|
||||||
|
if ((nodep->joinType().joinNone() && !nodep->immediateStart())
|
||||||
|
|| forkIsDisableable(nodep)) {
|
||||||
UINFO(9, "Adding fork branch start sentinels " << nodep);
|
UINFO(9, "Adding fork branch start sentinels " << nodep);
|
||||||
FileLine* fl = nodep->fileline();
|
FileLine* fl = nodep->fileline();
|
||||||
// We use a sentinel value of UINT64_MAX to mark this delay so that it goes to the
|
// We use a sentinel value of UINT64_MAX to mark this delay so that it goes to the
|
||||||
|
|
|
||||||
|
|
@ -528,6 +528,7 @@ class HasherVisitor final : public VNVisitorConst {
|
||||||
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
|
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { //
|
||||||
m_hash += nodep->name();
|
m_hash += nodep->name();
|
||||||
m_hash += nodep->joinType();
|
m_hash += nodep->joinType();
|
||||||
|
m_hash += nodep->immediateStart();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void visit(AstPin* nodep) override {
|
void visit(AstPin* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ def covergroup_coverage_report(test, outfile=None):
|
||||||
return outfile
|
return outfile
|
||||||
|
|
||||||
|
|
||||||
def run(test, *, verilator_flags2=()):
|
def run(test, *, verilator_flags2=(), timing_loop=False):
|
||||||
test.compile(verilator_flags2=['--coverage', *verilator_flags2])
|
test.compile(verilator_flags2=['--coverage', *verilator_flags2], timing_loop=timing_loop)
|
||||||
test.execute()
|
test.execute()
|
||||||
covergroup_coverage_report(test)
|
covergroup_coverage_report(test)
|
||||||
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
|
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ GlobalCg.cp_global.auto_8: 0
|
||||||
GlobalCg.cp_global.auto_9: 0
|
GlobalCg.cp_global.auto_9: 0
|
||||||
__vlAnonCG_base_cg.cp_base.hi: 8
|
__vlAnonCG_base_cg.cp_base.hi: 8
|
||||||
__vlAnonCG_base_cg.cp_base.lo: 8
|
__vlAnonCG_base_cg.cp_base.lo: 8
|
||||||
|
__vlAnonCG_both_cg.cp_both.hi: 0
|
||||||
|
__vlAnonCG_both_cg.cp_both.lo: 4
|
||||||
__vlAnonCG_branch_cg.cp_branch.hi: 8
|
__vlAnonCG_branch_cg.cp_branch.hi: 8
|
||||||
__vlAnonCG_branch_cg.cp_branch.lo: 8
|
__vlAnonCG_branch_cg.cp_branch.lo: 8
|
||||||
__vlAnonCG_cg.cp_base.auto_0: 0
|
__vlAnonCG_cg.cp_base.auto_0: 0
|
||||||
|
|
@ -50,6 +52,10 @@ __vlAnonCG_cg.cp_derived.auto_6: 0
|
||||||
__vlAnonCG_cg.cp_derived.auto_7: 0
|
__vlAnonCG_cg.cp_derived.auto_7: 0
|
||||||
__vlAnonCG_cg.cp_derived.auto_8: 0
|
__vlAnonCG_cg.cp_derived.auto_8: 0
|
||||||
__vlAnonCG_cg.cp_derived.auto_9: 0
|
__vlAnonCG_cg.cp_derived.auto_9: 0
|
||||||
|
__vlAnonCG_change_cg.cp_change.hi: 2
|
||||||
|
__vlAnonCG_change_cg.cp_change.lo: 4
|
||||||
|
__vlAnonCG_clock_cg.cp_clocked.hi: 8
|
||||||
|
__vlAnonCG_clock_cg.cp_clocked.lo: 8
|
||||||
__vlAnonCG_copy_cg.cp_copy.hi: 0
|
__vlAnonCG_copy_cg.cp_copy.hi: 0
|
||||||
__vlAnonCG_copy_cg.cp_copy.lo: 1
|
__vlAnonCG_copy_cg.cp_copy.lo: 1
|
||||||
__vlAnonCG_derived_cg.cp_cross.hi_x_hi [cross]: 0
|
__vlAnonCG_derived_cg.cp_cross.hi_x_hi [cross]: 0
|
||||||
|
|
@ -62,6 +68,8 @@ __vlAnonCG_derived_cg.cp_inherited.hi: 8
|
||||||
__vlAnonCG_derived_cg.cp_inherited.lo: 8
|
__vlAnonCG_derived_cg.cp_inherited.lo: 8
|
||||||
__vlAnonCG_derived_cg.cp_this_inherited.hi: 8
|
__vlAnonCG_derived_cg.cp_this_inherited.hi: 8
|
||||||
__vlAnonCG_derived_cg.cp_this_inherited.lo: 8
|
__vlAnonCG_derived_cg.cp_this_inherited.lo: 8
|
||||||
|
__vlAnonCG_edge_cg.cp_edge.hi: 0
|
||||||
|
__vlAnonCG_edge_cg.cp_edge.lo: 4
|
||||||
__vlAnonCG_first_cg.cp_first.hi: 8
|
__vlAnonCG_first_cg.cp_first.hi: 8
|
||||||
__vlAnonCG_first_cg.cp_first.lo: 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_hi [cross]: 0
|
||||||
|
|
@ -92,6 +100,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_1: 4
|
||||||
__vlAnonCG_mon_cg.cp_op_b.auto_2: 4
|
__vlAnonCG_mon_cg.cp_op_b.auto_2: 4
|
||||||
__vlAnonCG_mon_cg.cp_op_b.auto_3: 4
|
__vlAnonCG_mon_cg.cp_op_b.auto_3: 4
|
||||||
|
__vlAnonCG_neg_cg.cp_neg.hi: 0
|
||||||
|
__vlAnonCG_neg_cg.cp_neg.lo: 2
|
||||||
__vlAnonCG_nested_cg.cp_container.hi: 8
|
__vlAnonCG_nested_cg.cp_container.hi: 8
|
||||||
__vlAnonCG_nested_cg.cp_container.lo: 8
|
__vlAnonCG_nested_cg.cp_container.lo: 8
|
||||||
__vlAnonCG_nested_cg.cp_local.hi: 8
|
__vlAnonCG_nested_cg.cp_local.hi: 8
|
||||||
|
|
@ -100,6 +110,8 @@ __vlAnonCG_nested_cg.cp_static.hi: 8
|
||||||
__vlAnonCG_nested_cg.cp_static.lo: 8
|
__vlAnonCG_nested_cg.cp_static.lo: 8
|
||||||
__vlAnonCG_parameterized_cg.cp_parameterized.hi: 8
|
__vlAnonCG_parameterized_cg.cp_parameterized.hi: 8
|
||||||
__vlAnonCG_parameterized_cg.cp_parameterized.lo: 8
|
__vlAnonCG_parameterized_cg.cp_parameterized.lo: 8
|
||||||
|
__vlAnonCG_pos_cg.cp_pos.hi: 0
|
||||||
|
__vlAnonCG_pos_cg.cp_pos.lo: 2
|
||||||
__vlAnonCG_second_cg.cp_second.hi: 8
|
__vlAnonCG_second_cg.cp_second.hi: 8
|
||||||
__vlAnonCG_second_cg.cp_second.lo: 8
|
__vlAnonCG_second_cg.cp_second.lo: 8
|
||||||
__vlAnonCG_static_cg.cp_instance.hi: 8
|
__vlAnonCG_static_cg.cp_instance.hi: 8
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,75 @@ class ThisHandleMonitor;
|
||||||
endclass
|
endclass
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
|
class ClockEvent;
|
||||||
|
bit clk;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class ClockMonitor;
|
||||||
|
ClockEvent ev;
|
||||||
|
bit [3:0] sampled;
|
||||||
|
|
||||||
|
covergroup clock_cg @(posedge ev.clk);
|
||||||
|
cp_clocked: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
ev = new;
|
||||||
|
ev.clk = 0;
|
||||||
|
clock_cg = new;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function void observe(bit [3:0] v);
|
||||||
|
sampled = v;
|
||||||
|
ev.clk = 0;
|
||||||
|
ev.clk = 1;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class VectorClockMonitor;
|
||||||
|
bit [2:0] clk_vec;
|
||||||
|
bit [3:0] sampled;
|
||||||
|
|
||||||
|
covergroup pos_cg @(posedge clk_vec);
|
||||||
|
cp_pos: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
covergroup neg_cg @(negedge clk_vec);
|
||||||
|
cp_neg: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
covergroup both_cg @(posedge clk_vec or negedge clk_vec);
|
||||||
|
cp_both: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
covergroup edge_cg @(edge clk_vec);
|
||||||
|
cp_edge: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
covergroup change_cg @(clk_vec);
|
||||||
|
cp_change: coverpoint sampled {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
clk_vec = 3'b000;
|
||||||
|
pos_cg = new;
|
||||||
|
neg_cg = new;
|
||||||
|
both_cg = new;
|
||||||
|
edge_cg = new;
|
||||||
|
change_cg = new;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function void observe(bit [2:0] next_clk, bit [3:0] value);
|
||||||
|
sampled = value;
|
||||||
|
clk_vec = next_clk;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function void observe_bit(bit next_clk, bit [3:0] value);
|
||||||
|
sampled = value;
|
||||||
|
clk_vec[0] = next_clk;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
class CopyMonitor;
|
class CopyMonitor;
|
||||||
bit [3:0] value;
|
bit [3:0] value;
|
||||||
|
|
||||||
|
|
@ -412,6 +481,8 @@ module t;
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
ThisHandleMonitor this_handle_mon;
|
ThisHandleMonitor this_handle_mon;
|
||||||
`endif
|
`endif
|
||||||
|
ClockMonitor clock_mon;
|
||||||
|
VectorClockMonitor vector_clock_mon;
|
||||||
CopyMonitor copy_src;
|
CopyMonitor copy_src;
|
||||||
CopyMonitor copy_dst;
|
CopyMonitor copy_dst;
|
||||||
GlobalCgHolder global_src;
|
GlobalCgHolder global_src;
|
||||||
|
|
@ -439,6 +510,8 @@ module t;
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
this_handle_mon = new;
|
this_handle_mon = new;
|
||||||
`endif
|
`endif
|
||||||
|
clock_mon = new;
|
||||||
|
vector_clock_mon = new;
|
||||||
copy_src = new;
|
copy_src = new;
|
||||||
global_src = new;
|
global_src = new;
|
||||||
clone_src = new;
|
clone_src = new;
|
||||||
|
|
@ -460,26 +533,44 @@ module t;
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
this_handle_mon.observe(i[3:0]);
|
this_handle_mon.observe(i[3:0]);
|
||||||
`endif
|
`endif
|
||||||
|
clock_mon.observe(i[3:0]);
|
||||||
static_mon.observe(i[3:0]);
|
static_mon.observe(i[3:0]);
|
||||||
static_only_mon.observe(i[3:0]);
|
static_only_mon.observe(i[3:0]);
|
||||||
multiple_mon.observe(i[3:0], 15 - i[3:0]);
|
multiple_mon.observe(i[3:0], 15 - i[3:0]);
|
||||||
nested_mon.observe(i[3:0]);
|
nested_mon.observe(i[3:0]);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vector_clock_mon.observe(3'b010, 4'hf);
|
||||||
|
vector_clock_mon.observe(3'b011, 4'h1);
|
||||||
|
vector_clock_mon.observe(3'b010, 4'h2);
|
||||||
|
vector_clock_mon.observe(3'b000, 4'he);
|
||||||
|
vector_clock_mon.observe_bit(1'b1, 4'h3);
|
||||||
|
vector_clock_mon.observe_bit(1'b0, 4'h4);
|
||||||
|
|
||||||
for (i = 0; i < 8; ++i) begin
|
for (i = 0; i < 8; ++i) begin
|
||||||
branch_a.observe(i[2:0]);
|
branch_a.observe(i[2:0]);
|
||||||
branch_b.observe(i[2:0]);
|
branch_b.observe(i[2:0]);
|
||||||
end
|
end
|
||||||
|
|
||||||
copy_src.observe(4'h1);
|
copy_src.observe(4'h1);
|
||||||
|
`ifdef VERILATOR
|
||||||
|
// IEEE 1800-2023 8.12 requires embedded covergroups to be null after a shallow copy.
|
||||||
|
// No new coverage object is created, so the copied object's properties are not covered.
|
||||||
|
// Questa instead aliases the source coverage object; Xcelium retains a non-null handle
|
||||||
|
// whose source and copied instances both report zero coverage.
|
||||||
copy_dst = new copy_src;
|
copy_dst = new copy_src;
|
||||||
`checkd(copy_dst.copy_cg == null, 1);
|
`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_dst = new clone_src;
|
||||||
clone_base_view = clone_dst;
|
clone_base_view = clone_dst;
|
||||||
`checkd(clone_dst.cg == null, 1);
|
`checkd(clone_dst.cg == null, 1);
|
||||||
`checkd(clone_base_view.cg == null, 1);
|
`checkd(clone_base_view.cg == null, 1);
|
||||||
|
`endif
|
||||||
|
|
||||||
|
global_dst = new global_src;
|
||||||
|
`ifndef NC
|
||||||
|
// Comparing a covergroup variable with a non-null value is unsupported in Xcelium.
|
||||||
|
`checkd(global_dst.cg == global_src.cg, 1);
|
||||||
|
`endif
|
||||||
|
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error: t/t_covergroup_embedded_event_bad.v:31:8: Embedded covergroup variable 'cg' may only be assigned in the enclosing class's 'new' method (IEEE 1800-2023 19.4).
|
||||||
|
: ... note: In instance 't'
|
||||||
|
31 | cg = new;
|
||||||
|
| ^
|
||||||
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/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('vlt')
|
||||||
|
|
||||||
|
test.compile(
|
||||||
|
verilator_flags2=['--no-timing'],
|
||||||
|
fails=True,
|
||||||
|
expect_filename=test.golden_filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// 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 Level;
|
||||||
|
bit event_signal;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Middle;
|
||||||
|
Level level;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Monitor;
|
||||||
|
bit clk;
|
||||||
|
bit [3:0] value;
|
||||||
|
Middle middle;
|
||||||
|
|
||||||
|
covergroup cg @(posedge clk or posedge middle.level.event_signal);
|
||||||
|
cp: coverpoint value;
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
middle = new;
|
||||||
|
middle.level = new;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function void build();
|
||||||
|
cg = new;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t;
|
||||||
|
Monitor mon;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
mon = new;
|
||||||
|
mon.build();
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
%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)
|
%Error-ENCAPSULATED: t/t_covergroup_embedded_nested_bad.v:44:20: 'local_value' is hidden as 'local' within this context (IEEE 1800-2023 8.18)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
42 | cp: coverpoint local_value;
|
44 | cp: coverpoint local_value;
|
||||||
| ^~~~~~~~~~~
|
| ^~~~~~~~~~~
|
||||||
t/t_covergroup_embedded_nested_bad.v:42:20: ... Location of definition
|
t/t_covergroup_embedded_nested_bad.v:44:20: ... Location of definition
|
||||||
37 | local bit [3:0] local_value;
|
39 | local bit [3:0] local_value;
|
||||||
| ^~~~~~~~~~~
|
| ^~~~~~~~~~~
|
||||||
... For error description see https://verilator.org/warn/ENCAPSULATED?v=latest
|
... 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).
|
%Error-ENCAPSULATED: t/t_covergroup_embedded_nested_bad.v:57:27: 'local_event' is hidden as 'local' within this context (IEEE 1800-2023 8.18)
|
||||||
|
: ... note: In instance 't'
|
||||||
|
57 | covergroup cg @(posedge local_event);
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
t/t_covergroup_embedded_nested_bad.v:57:27: ... Location of definition
|
||||||
|
53 | local bit local_event;
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_covergroup_embedded_nested_bad.v:15:22: Non-static member 'outer_value' of an outer class requires an explicit object handle (IEEE 1800-2023 8.23).
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
13 | cp: coverpoint outer_value;
|
15 | cp: coverpoint outer_value;
|
||||||
| ^~~~~~~~~~~
|
| ^~~~~~~~~~~
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
... 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).
|
%Error: t/t_covergroup_embedded_nested_bad.v:27:39: Non-static member 'outer_value' of an outer class requires an explicit object handle (IEEE 1800-2023 8.23).
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
25 | cp: coverpoint inner_value iff (outer_value != 0);
|
27 | cp: coverpoint inner_value iff (outer_value != 0);
|
||||||
| ^~~~~~~~~~~
|
| ^~~~~~~~~~~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('linter')
|
test.scenarios('linter')
|
||||||
|
|
||||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename)
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ class Outer;
|
||||||
bit [3:0] outer_value;
|
bit [3:0] outer_value;
|
||||||
|
|
||||||
class CoverpointInner;
|
class CoverpointInner;
|
||||||
covergroup cg;
|
bit clk;
|
||||||
|
|
||||||
|
covergroup cg @(posedge clk);
|
||||||
cp: coverpoint outer_value;
|
cp: coverpoint outer_value;
|
||||||
endgroup
|
endgroup
|
||||||
|
|
||||||
|
|
@ -47,14 +49,30 @@ class LocalDerived extends LocalBase;
|
||||||
endfunction
|
endfunction
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
|
class LocalEventBase;
|
||||||
|
local bit local_event;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class LocalEventDerived extends LocalEventBase;
|
||||||
|
covergroup cg @(posedge local_event);
|
||||||
|
cp: coverpoint 1'b1;
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
cg = new;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
module t;
|
module t;
|
||||||
Outer::CoverpointInner coverpoint_inner;
|
Outer::CoverpointInner coverpoint_inner;
|
||||||
Outer::IffInner iff_inner;
|
Outer::IffInner iff_inner;
|
||||||
LocalDerived local_derived;
|
LocalDerived local_derived;
|
||||||
|
LocalEventDerived local_event_derived;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
coverpoint_inner = new;
|
coverpoint_inner = new;
|
||||||
iff_inner = new;
|
iff_inner = new;
|
||||||
local_derived = new;
|
local_derived = new;
|
||||||
|
local_event_derived = new;
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
__vlAnonCG_chain_cg.cp_val.hi: 4
|
||||||
|
__vlAnonCG_chain_cg.cp_val.lo: 3
|
||||||
|
__vlAnonCG_ext_cg.cp_val.hi: 8
|
||||||
|
__vlAnonCG_ext_cg.cp_val.lo: 1
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
coverage_covergroup_common.run(test, verilator_flags2=['--timing'], timing_loop=True)
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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 ExtClkMonitor;
|
||||||
|
bit clk;
|
||||||
|
bit [3:0] value;
|
||||||
|
|
||||||
|
covergroup ext_cg @(posedge clk);
|
||||||
|
cp_val: coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
ext_cg = new;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Lvl;
|
||||||
|
bit ev;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Mid;
|
||||||
|
Lvl lvl;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class ChainMonitor;
|
||||||
|
bit a;
|
||||||
|
Mid mid;
|
||||||
|
bit [3:0] value;
|
||||||
|
|
||||||
|
covergroup chain_cg @(posedge a or posedge mid.lvl.ev);
|
||||||
|
cp_val: coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
mid = new;
|
||||||
|
mid.lvl = new;
|
||||||
|
chain_cg = new;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t;
|
||||||
|
ExtClkMonitor extm;
|
||||||
|
ChainMonitor chain;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
extm = new;
|
||||||
|
extm.value = 4'h3;
|
||||||
|
// This edge must be sampled before the caller first blocks at #1 (IEEE 1800-2023 19.3);
|
||||||
|
// normal fork-join_none startup deferral (IEEE 1800-2023 9.3.2) would miss it.
|
||||||
|
extm.clk = 1'b1;
|
||||||
|
#1;
|
||||||
|
chain = new;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) begin
|
||||||
|
extm.value = 4'h8 | i[3:0];
|
||||||
|
extm.clk = 1'b0;
|
||||||
|
#1;
|
||||||
|
extm.clk = 1'b1;
|
||||||
|
#1;
|
||||||
|
end
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i) begin
|
||||||
|
chain.value = 4'hc;
|
||||||
|
chain.a = 1'b0;
|
||||||
|
#1;
|
||||||
|
chain.a = 1'b1;
|
||||||
|
#1;
|
||||||
|
end
|
||||||
|
for (i = 0; i < 3; ++i) begin
|
||||||
|
chain.value = 4'h2;
|
||||||
|
chain.mid.lvl.ev = 1'b0;
|
||||||
|
#1;
|
||||||
|
chain.mid.lvl.ev = 1'b1;
|
||||||
|
#1;
|
||||||
|
end
|
||||||
|
|
||||||
|
#1;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:44:23: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_param'
|
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:45:23: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_param'
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
44 | cp: coverpoint st.test;
|
45 | cp: coverpoint st.test;
|
||||||
| ^~~~
|
| ^~~~
|
||||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
... 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.
|
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:60:37: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_mixed'
|
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:61:37: Unsupported: 'covergroup' coverpoint dereferencing a class handle member; ignoring covergroup '__vlAnonCG_cov_mixed'
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
60 | cp: coverpoint local_value + st.test;
|
61 | cp: coverpoint local_value + st.test;
|
||||||
| ^~~~
|
| ^~~~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('vlt')
|
test.scenarios('vlt')
|
||||||
|
|
||||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
test.lint(verilator_flags2=['--timing'], expect_filename=test.golden_filename, fails=True)
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,12 @@ endclass
|
||||||
|
|
||||||
class parameterized_monitor;
|
class parameterized_monitor;
|
||||||
coverage_state cs;
|
coverage_state cs;
|
||||||
|
bit clk;
|
||||||
|
|
||||||
// Parameterized covergroup: the coverpoints dereference the class-handle argument 'st'.
|
// Parameterized covergroup: the coverpoints dereference the class-handle argument 'st'.
|
||||||
// Two handle-dereferencing coverpoints ensure the safety net reports only the first
|
// Two handle-dereferencing coverpoints ensure the safety net reports only the first
|
||||||
// offender (a second AstMemberSel is seen with the offender already latched).
|
// offender (a second AstMemberSel is seen with the offender already latched).
|
||||||
covergroup cov_param(coverage_state st);
|
covergroup cov_param(coverage_state st) @(posedge clk);
|
||||||
cp: coverpoint st.test;
|
cp: coverpoint st.test;
|
||||||
cp2: coverpoint st.test2;
|
cp2: coverpoint st.test2;
|
||||||
endgroup
|
endgroup
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:13:5: Unsupported: 'covergroup' clocking event on member variable
|
%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.
|
||||||
: ... note: In instance 't'
|
15 | covergroup cov_extclk @(posedge clk);
|
||||||
13 | covergroup cov1 @m_z;
|
| ^~~~~~~
|
||||||
| ^~~~~~~~~~
|
|
||||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
... 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.
|
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||||
|
%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'
|
||||||
|
37 | covergroup cov_cplx @(posedge a or posedge mid.lvl.ev);
|
||||||
|
| ^~~~~~~~~~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('vlt')
|
test.scenarios('vlt')
|
||||||
|
|
||||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
# Dynamic per-instance event waits require --timing. Without it, simple events use
|
||||||
|
# best-effort in-class assignment instrumentation and unsupported cases warn.
|
||||||
|
test.lint(verilator_flags2=['--no-timing'], expect_filename=test.golden_filename, fails=True)
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,53 @@
|
||||||
// SPDX-FileCopyrightText: 2024 Wilson Snyder
|
// SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
module t (
|
// Embedded covergroup events that cannot be approximated without --timing must
|
||||||
input clk
|
// emit COVERIGN rather than silently producing zero coverage.
|
||||||
);
|
|
||||||
class Packet;
|
module t;
|
||||||
int m_z;
|
class ExternalClk;
|
||||||
int m_x;
|
bit clk;
|
||||||
covergroup cov1 @m_z;
|
bit [3:0] value;
|
||||||
coverpoint m_x;
|
|
||||||
|
covergroup cov_extclk @(posedge clk);
|
||||||
|
coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||||
endgroup
|
endgroup
|
||||||
|
|
||||||
|
function new();
|
||||||
|
cov_extclk = new;
|
||||||
|
endfunction
|
||||||
endclass
|
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
|
||||||
|
|
||||||
|
ExternalClk ec;
|
||||||
|
ComplexEvt cx;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
|
ec = new;
|
||||||
|
cx = new;
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,3 @@
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
177 | cross a, b {
|
177 | cross a, b {
|
||||||
| ^
|
| ^
|
||||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:209:5: Unsupported: 'covergroup' clocking event on member variable
|
|
||||||
: ... note: In instance 't'
|
|
||||||
209 | covergroup cov1 @m_z;
|
|
||||||
| ^~~~~~~~~~
|
|
||||||
|
|
|
||||||
|
|
@ -1228,12 +1228,13 @@ package Vt_debug_emitv_std;
|
||||||
endpackage
|
endpackage
|
||||||
package Vt_debug_emitv___024unit;
|
package Vt_debug_emitv___024unit;
|
||||||
class Vt_debug_emitv_Cls;
|
class Vt_debug_emitv_Cls;
|
||||||
|
bit cg_clk;
|
||||||
int signed member;
|
int signed member;
|
||||||
member = 'sh1;
|
member = 'sh1;
|
||||||
int signed rmember1;
|
int signed rmember1;
|
||||||
int signed rmember2;
|
int signed rmember2;
|
||||||
covergroup Vt_debug_emitv___vlAnonCG_cg_in_class;
|
covergroup Vt_debug_emitv___vlAnonCG_cg_in_class;
|
||||||
function new;
|
@(posedge cg_clk)function new;
|
||||||
cp_m: coverpoint member {
|
cp_m: coverpoint member {
|
||||||
bins one = {'sh1};
|
bins one = {'sh1};
|
||||||
bins two = {'sh2};
|
bins two = {'sh2};
|
||||||
|
|
@ -1283,13 +1284,14 @@ package Vt_debug_emitv___024unit;
|
||||||
endfunction
|
endfunction
|
||||||
endcovergroup
|
endcovergroup
|
||||||
Vt_debug_emitv___vlAnonCG_cg_in_class cg_in_classVt_debug_emitv___vlAnonCG_cg_in_class;
|
Vt_debug_emitv___vlAnonCG_cg_in_class cg_in_classVt_debug_emitv___vlAnonCG_cg_in_class;
|
||||||
|
function new;
|
||||||
|
cg_in_class = new();
|
||||||
|
endfunction
|
||||||
task method;
|
task method;
|
||||||
if ((this != this)) begin
|
if ((this != this)) begin
|
||||||
$stop;
|
$stop;
|
||||||
end
|
end
|
||||||
endtask
|
endtask
|
||||||
function new;
|
|
||||||
endfunction
|
|
||||||
function randomize;
|
function randomize;
|
||||||
endfunction
|
endfunction
|
||||||
endclass
|
endclass
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ test.lint(
|
||||||
# unsupported (dropped) but the bin is still created with a non-NONE VTransRepType, which
|
# unsupported (dropped) but the bin is still created with a non-NONE VTransRepType, which
|
||||||
# is what exercises VTransRepType::ascii() and AstCoverTransItem::dump()'s repType arm.
|
# is what exercises VTransRepType::ascii() and AstCoverTransItem::dump()'s repType arm.
|
||||||
"--Wno-COVERIGN",
|
"--Wno-COVERIGN",
|
||||||
|
"--timing",
|
||||||
"--dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv", # Dev coverage of the V3EmitV code
|
"--dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv", # Dev coverage of the V3EmitV code
|
||||||
"--dump-graph --dumpi-tree-json 9 --no-json-ids"
|
"--dump-graph --dumpi-tree-json 9 --no-json-ids"
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,19 @@ package PkgImp;
|
||||||
endpackage
|
endpackage
|
||||||
|
|
||||||
class Cls;
|
class Cls;
|
||||||
|
bit cg_clk;
|
||||||
int member = 1;
|
int member = 1;
|
||||||
rand int rmember1;
|
rand int rmember1;
|
||||||
rand int rmember2;
|
rand int rmember2;
|
||||||
covergroup cg_in_class;
|
covergroup cg_in_class @(posedge cg_clk);
|
||||||
cp_m: coverpoint member {
|
cp_m: coverpoint member {
|
||||||
bins one = {1};
|
bins one = {1};
|
||||||
bins two = {2};
|
bins two = {2};
|
||||||
}
|
}
|
||||||
endgroup
|
endgroup
|
||||||
|
function new;
|
||||||
|
cg_in_class = new;
|
||||||
|
endfunction
|
||||||
function void method;
|
function void method;
|
||||||
if (this != this) $stop;
|
if (this != this) $stop;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,6 @@ test.lint(
|
||||||
# --Wno-COVERIGN: shares t_debug_emitv.v, whose cg_trans uses a goto-repetition transition
|
# --Wno-COVERIGN: shares t_debug_emitv.v, whose cg_trans uses a goto-repetition transition
|
||||||
# bin ([->N]); the count is unsupported (dropped) but the bin is still created with a
|
# bin ([->N]); the count is unsupported (dropped) but the bin is still created with a
|
||||||
# non-NONE VTransRepType.
|
# non-NONE VTransRepType.
|
||||||
v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids --Wno-COVERIGN"])
|
v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids --Wno-COVERIGN --timing"])
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue