From 0820506d7459db5c2f2ac8a3163dbf05cf4e15b4 Mon Sep 17 00:00:00 2001 From: Marco Bartoli Date: Sat, 22 Aug 2026 21:46:07 +0200 Subject: [PATCH] Support covergroup/function/task (ref/virtual/array) interface arguments (#8180) --- src/V3Ast.cpp | 44 +- src/V3Ast.h | 2 +- src/V3AstInlines.h | 3 +- src/V3AstNodeDType.h | 3 +- src/V3AstNodeOther.h | 4 + src/V3AstNodes.cpp | 10 +- src/V3Covergroup.cpp | 248 ++++++-- src/V3EmitCBase.cpp | 6 + src/V3EmitCFunc.h | 4 + src/V3EmitCHeaders.cpp | 2 +- src/V3LinkDot.cpp | 6 +- src/V3LinkParse.cpp | 33 +- src/V3Width.cpp | 34 +- test_regress/t/t_castdyn_unsup_bad.out | 14 +- test_regress/t/t_castdyn_unsup_bad.v | 11 + test_regress/t/t_covergroup_args.out | 148 ++++- test_regress/t/t_covergroup_args.v | 544 +++++++++++++++++- test_regress/t/t_covergroup_args_bad.out | 26 + ...dded_unsup.py => t_covergroup_args_bad.py} | 0 test_regress/t/t_covergroup_args_bad.v | 45 ++ test_regress/t/t_covergroup_args_expr_bad.out | 48 ++ test_regress/t/t_covergroup_args_expr_bad.py | 16 + test_regress/t/t_covergroup_args_expr_bad.v | 44 ++ test_regress/t/t_covergroup_args_type_bad.out | 54 ++ test_regress/t/t_covergroup_args_type_bad.py | 16 + test_regress/t/t_covergroup_args_type_bad.v | 86 +++ .../t/t_covergroup_embedded_unsup.out | 7 - test_regress/t/t_covergroup_embedded_unsup.v | 34 -- .../t/t_covergroup_member_event_unsup.out | 20 +- .../t/t_covergroup_member_event_unsup.py | 9 +- .../t/t_covergroup_member_event_unsup.v | 37 ++ .../t/t_covergroup_ref_default_unsup.out | 8 + .../t/t_covergroup_ref_default_unsup.py | 16 + .../t/t_covergroup_ref_default_unsup.v | 20 + test_regress/t/t_debug_emitv.out | 51 ++ test_regress/t/t_debug_emitv.v | 3 + test_regress/t/t_interface_virtual.v | 29 +- test_regress/t/t_interface_virtual_bad.out | 44 ++ test_regress/t/t_interface_virtual_bad.v | 37 ++ 39 files changed, 1630 insertions(+), 136 deletions(-) create mode 100644 test_regress/t/t_covergroup_args_bad.out rename test_regress/t/{t_covergroup_embedded_unsup.py => t_covergroup_args_bad.py} (100%) create mode 100644 test_regress/t/t_covergroup_args_bad.v create mode 100644 test_regress/t/t_covergroup_args_expr_bad.out create mode 100755 test_regress/t/t_covergroup_args_expr_bad.py create mode 100644 test_regress/t/t_covergroup_args_expr_bad.v create mode 100644 test_regress/t/t_covergroup_args_type_bad.out create mode 100755 test_regress/t/t_covergroup_args_type_bad.py create mode 100644 test_regress/t/t_covergroup_args_type_bad.v delete mode 100644 test_regress/t/t_covergroup_embedded_unsup.out delete mode 100644 test_regress/t/t_covergroup_embedded_unsup.v create mode 100644 test_regress/t/t_covergroup_ref_default_unsup.out create mode 100755 test_regress/t/t_covergroup_ref_default_unsup.py create mode 100644 test_regress/t/t_covergroup_ref_default_unsup.v diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index d005c5bcd..112482bd8 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1660,7 +1660,7 @@ static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) { } static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, - const AstNode* fromConstp) { + const AstNode* fromConstp, const bool checkIfaceArgCompat) { const VCastable castable = VCastable::UNSUPPORTED; toDtp = toDtp->skipRefToEnump(); fromDtp = fromDtp->skipRefToEnump(); @@ -1698,22 +1698,52 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp if (upcast) return VCastable::COMPATIBLE; if (downcast) return VCastable::DYNAMIC_CLASS; return VCastable::INCOMPATIBLE; - } else if (const AstIfaceRefDType* const toIfp = VN_CAST(toDtp, IfaceRefDType)) { + } else if (const AstIfaceRefDType* const toIfp + = VN_CAST(checkIfaceArgCompat ? toDtp->elemDTypep(true) : toDtp, IfaceRefDType)) { // Two interface refs are compatible if they point at the same interface // module (and modport, if any). Pointer-equality on the dtype isn't // enough since every cell binding clones the dtype. - const AstIfaceRefDType* const fromIfp = VN_CAST(fromDtp, IfaceRefDType); - if (fromIfp && toIfp->ifaceViaCellp() == fromIfp->ifaceViaCellp() - && (!toIfp->modportp() || toIfp->modportp() == fromIfp->modportp())) { + // Argument compatibility also requires matching virtualness for ref arguments, while + // input arguments may bind an unqualified or same-modport source to a virtual target. + // Argument compatibility also supports fixed-size arrays of interface references. + const AstIfaceRefDType* const fromIfp + = VN_CAST(checkIfaceArgCompat ? fromDtp->elemDTypep(true) : fromDtp, IfaceRefDType); + if (checkIfaceArgCompat && fromIfp && (toDtp != toIfp || fromDtp != fromIfp)) { + const AstUnpackArrayDType* const toArrayp = VN_CAST(toDtp, UnpackArrayDType); + const AstUnpackArrayDType* const fromArrayp = VN_CAST(fromDtp, UnpackArrayDType); + // IEEE 1800-2023 6.22.2: Equal-sized fixed arrays have equivalent types. + if (!toArrayp || !fromArrayp + || toArrayp->elementsConst() != fromArrayp->elementsConst()) { + return VCastable::INCOMPATIBLE; + } + return computeCastableImp(toArrayp->subDTypep(), fromArrayp->subDTypep(), nullptr, + checkIfaceArgCompat); + } + if (!fromIfp || toIfp->ifaceViaCellp() != fromIfp->ifaceViaCellp()) { + if (!checkIfaceArgCompat) return castable; + return VCastable::INCOMPATIBLE; + } + const bool sameModport = toIfp->modportp() == fromIfp->modportp(); + if (!checkIfaceArgCompat) { + if (!toIfp->modportp() || sameModport) return VCastable::COMPATIBLE; + return castable; + } + if (toIfp->isVirtual() == fromIfp->isVirtual() && sameModport) { + return VCastable::SAMEISH; + } + // An unqualified interface or virtual interface may bind to a modport-qualified + // virtual interface. + if (toIfp->isVirtual() && (!fromIfp->modportp() || sameModport)) { return VCastable::COMPATIBLE; } + return VCastable::INCOMPATIBLE; } return castable; } VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, - const AstNode* fromConstp) { - const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp); + const AstNode* fromConstp, const bool checkIfaceArgCompat) { + const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp, checkIfaceArgCompat); UINFO(9, " castable=" << castable << " for " << toDtp); UINFO(9, " =?= " << fromDtp); if (fromConstp) UINFO(9, " const= " << fromConstp); diff --git a/src/V3Ast.h b/src/V3Ast.h index fc3d277e5..404995928 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -809,7 +809,7 @@ public: static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); static VCastable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp, - const AstNode* fromConstp); + const AstNode* fromConstp, bool checkIfaceArgCompat = false); static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2); // METHODS - dump and error diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 6ef16eec6..0dd1f5953 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -160,7 +160,8 @@ AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* bool AstVar::sameNode(const AstNode* samep) const { const AstVar* const asamep = VN_DBG_AS(samep, Var); - return m_name == asamep->m_name && varType() == asamep->varType(); + return m_name == asamep->m_name && varType() == asamep->varType() + && covergroupRefMember() == asamep->covergroupRefMember(); } AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp) diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 9ee73a84c..52bbe6667 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -923,7 +923,8 @@ public: : ASTGEN_SUPER_IfaceRefDType(fl) , m_modportFileline{modportFl} , m_cellName{cellName} - , m_ifaceName{ifaceName} { + , m_ifaceName{ifaceName} + , m_modportName{modport} { addParamsp(paramsp); } ASTGEN_MEMBERS_AstIfaceRefDType; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index d9ad80854..589311f61 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2153,6 +2153,7 @@ class AstVar final : public AstNode { bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment bool m_constPoolEntry : 1; // Constant pool variable + bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument bool m_fileDescr : 1; // File descriptor bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect @@ -2217,6 +2218,7 @@ class AstVar final : public AstNode { m_attrFsmResetArc = false; m_attrFsmArcInclCond = false; m_constPoolEntry = false; + m_covergroupRefMember = false; m_fileDescr = false; m_gotNansiType = false; m_icoMaybeWritten = false; @@ -2368,6 +2370,8 @@ public: void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; } bool constPoolEntry() const { return m_constPoolEntry; } void setConstPoolEntry() { m_constPoolEntry = true; } + bool covergroupRefMember() const { return m_covergroupRefMember; } + void covergroupRefMember() { m_covergroupRefMember = true; } void rand(const VRandAttr flag) { m_rand = flag; } void usedParam(bool flag) { m_usedParam = flag; } void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7868d2637..f9fd06ad8 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -717,13 +717,17 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef())); - if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const "; - string oname; if (named) { if (!namespc.empty()) oname += namespc + "::"; oname += VIdProtect::protectIf(name(), protect()); } + if (forFunc && (isReadOnly() || constRef) && asRef) { + if (VN_IS(dtypep()->skipRefp(), IfaceRefDType)) { + return ostatic + dtypep()->cType("", forFunc, false) + " const &" + oname; + } + ostatic += "const "; + } return ostatic + dtypep()->cType(oname, forFunc, asRef); } @@ -3323,6 +3327,7 @@ int AstVarRef::instrCount() const { void AstVar::dump(std::ostream& str) const { this->AstNode::dump(str); if (constPoolEntry()) str << " [CONSTPOOL]"; + if (covergroupRefMember()) str << " [CGREF]"; if (isSc()) str << " [SC]"; if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]")); if (isPrimaryClock()) str << " [PCLK]"; @@ -3365,6 +3370,7 @@ void AstVar::dumpJson(std::ostream& str) const { dumpJsonStrFunc(str, origName); dumpJsonStrFunc(str, verilogName); dumpJsonBoolFuncIf(str, constPoolEntry); + dumpJsonBoolFuncIf(str, covergroupRefMember); dumpJsonBoolFuncIf(str, isSc); dumpJsonBoolFuncIf(str, isPrimaryIO); dumpJsonBoolFuncIf(str, isPrimaryClock); diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 7fe15f4a1..f838219a8 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -35,6 +35,89 @@ VL_DEFINE_DEBUG_FUNCTIONS; +//###################################################################### +// Covergroup expression validation visitor + +class CovergroupExprValidVisitor final : public VNVisitor { + const std::set& m_sampleMembers; + const std::set& m_constructorRefMembers; + bool m_inCoverageExpression = false; + bool m_sampleFormalAllowed = false; + + void scanSampleExpression(AstNode* nodep) { + if (!nodep) return; + VL_RESTORER(m_sampleFormalAllowed); + m_sampleFormalAllowed = true; + iterateAndNextNull(nodep); + } + + void scanCoverageExpression(AstNode* nodep) { + if (!nodep) return; + VL_RESTORER(m_inCoverageExpression); + m_inCoverageExpression = true; + iterateAndNextNull(nodep); + } + + void visit(AstCoverpoint* nodep) override { + scanSampleExpression(nodep->exprp()); + scanSampleExpression(nodep->iffp()); + iterateAndNextNull(nodep->binsp()); + iterateAndNextNull(nodep->optionsp()); + } + void visit(AstCoverCross* nodep) override { + iterateAndNextNull(nodep->itemsp()); + iterateAndNextNull(nodep->optionsp()); + iterateAndNextNull(nodep->rawBodyp()); + } + void visit(AstCoverBin* nodep) override { + scanCoverageExpression(nodep->rangesp()); + scanSampleExpression(nodep->iffp()); + scanCoverageExpression(nodep->arraySizep()); + scanCoverageExpression(nodep->transp()); + } + void visit(AstVarRef* nodep) override { + if (!m_sampleFormalAllowed && m_sampleMembers.count(nodep->varp())) { + nodep->v3error("Covergroup sample formal argument " + << nodep->varp()->prettyNameQ() + << " may only be used in a coverpoint or conditional guard " + "expression (IEEE 1800-2012 19.8.1)."); + } + if (m_inCoverageExpression && m_constructorRefMembers.count(nodep->varp())) { + nodep->v3error("Ref covergroup constructor formal argument " + << nodep->varp()->prettyNameQ() + << " may not be used in a covergroup expression " + "(IEEE 1800-2012 19.5)."); + } + } + void visit(AstNodeFTaskRef* nodep) override { + if (m_inCoverageExpression && nodep->taskp()) { + bool invalidDirection = false; + for (AstNode* stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + const AstVar* const varp = VN_CAST(stmtp, Var); + if (varp && varp->isIO() && varp->isWritable()) { + invalidDirection = true; + break; + } + } + if (invalidDirection) { + nodep->v3error("Function " << nodep->taskp()->prettyNameQ() + << " called in a covergroup expression has an " + "output, inout, or non-const ref argument " + "(IEEE 1800-2012 19.5)."); + } + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + CovergroupExprValidVisitor(const std::set& sampleMembers, + const std::set& constructorRefMembers) + : m_sampleMembers{sampleMembers} + , m_constructorRefMembers{constructorRefMembers} {} + void scan(AstNode* nodep) { iterate(nodep); } +}; + //###################################################################### // Functional coverage visitor @@ -484,7 +567,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { UINFO(4, " Generating code for coverpoint: " << coverpointp->name()); // Get the coverpoint expression - AstNodeExpr* const exprp = coverpointp->exprp(); + AstNodeExpr* exprp = coverpointp->exprp(); // Expand automatic bins before processing expandAutomaticBins(coverpointp, exprp); @@ -498,6 +581,18 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Create implicit automatic bins if no regular bins exist createImplicitAutoBins(coverpointp, exprp, autoBinMax); + AstVar* const valueVarp = new AstVar{ + coverpointp->fileline(), VVarType::BLOCKTEMP, + "__VcpValue_" + sanitizeGeneratedName(coverpointp->name()), exprp->dtypep()}; + valueVarp->funcLocal(true); + m_sampleFuncp->addStmtsp(valueVarp); + exprp->unlinkFrBack(); + m_sampleFuncp->addStmtsp(new AstAssign{ + coverpointp->fileline(), + new AstVarRef{coverpointp->fileline(), valueVarp, VAccess::WRITE}, exprp}); + coverpointp->exprp(new AstVarRef{coverpointp->fileline(), valueVarp, VAccess::READ}); + exprp = coverpointp->exprp(); + // Every coverpoint routes through the VlCoverpoint runtime. Transition coverpoints are // included: their per-value matching is still generated as a state machine in sample() // (see generateCoverpoint), but the bin hit is recorded in the runtime bin @@ -1358,10 +1453,12 @@ class FunctionalCoverageVisitor final : public VNVisitor { } else if (!boundp) { irp->v3error("Non-constant expression in bin range; " "range bounds must be constants"); + if (fullCondp) VL_DO_DANGLING(pushDeletep(fullCondp), fullCondp); return nullptr; } else if (boundp->num().isFourState()) { irp->v3error("Four-state (x/z) value in bin range bound; " "range bounds must be two-state constants"); + if (fullCondp) VL_DO_DANGLING(pushDeletep(fullCondp), fullCondp); return nullptr; } else { rangeCondp = makeOpenRangeCondition(irp->fileline(), exprp, boundp, @@ -1370,10 +1467,12 @@ class FunctionalCoverageVisitor final : public VNVisitor { } else if (!minConstp || !maxConstp) { irp->v3error("Non-constant expression in bin range; " "range bounds must be constants"); + if (fullCondp) VL_DO_DANGLING(pushDeletep(fullCondp), fullCondp); return nullptr; } else if (minConstp->num().isFourState() || maxConstp->num().isFourState()) { irp->v3error("Four-state (x/z) value in bin range bound; " "range bounds must be two-state constants"); + if (fullCondp) VL_DO_DANGLING(pushDeletep(fullCondp), fullCondp); return nullptr; } else if (minConstp->toUQuad() == maxConstp->toUQuad()) { // Single value @@ -1399,6 +1498,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { } else { currRangep->v3error( "Non-constant expression in bin range; values must be constants"); + if (fullCondp) VL_DO_DANGLING(pushDeletep(fullCondp), fullCondp); return nullptr; } @@ -1607,6 +1707,11 @@ class FunctionalCoverageVisitor final : public VNVisitor { return found; } + static bool hasInstanceMemberEventRef(const AstCovergroup* cgp) { + return cgp->eventp()->exists( + [](const AstVarRef* refp) { return isEnclosingInstanceVar(refp->varp()); }); + } + static bool parseEmbeddedEventExpr(AstNodeExpr* exprp, AstVar*& baseVarp, AstVar*& memberVarp) { if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { @@ -1758,62 +1863,90 @@ class FunctionalCoverageVisitor final : public VNVisitor { } class FormalRefVisitor final : public VNVisitor { - const std::set& m_constructorArgs; const std::map& m_replacements; - 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) return; const auto it = m_replacements.find(nodep->varp()); - if (it != m_replacements.end()) { - nodep->varp(it->second); - } else if (m_constructorArgs.count(nodep->varp())) { - m_offenderp = m_memberSelp; - } - } - void visit(AstNode* nodep) override { - if (!m_offenderp) iterateChildren(nodep); + if (it == m_replacements.end()) return; + nodep->varp(it->second); } + void visit(AstNode* nodep) override { iterateChildren(nodep); } public: - FormalRefVisitor(const std::set& constructorArgs, - const std::map& replacements) - : m_constructorArgs{constructorArgs} - , m_replacements{replacements} {} - void scan(AstNode* nodep) { - if (nodep && !m_offenderp) iterate(nodep); - } - AstNode* offenderp() const { return m_offenderp; } + explicit FormalRefVisitor(const std::map& replacements) + : m_replacements{replacements} {} + void scan(AstNode* nodep) { iterate(nodep); } }; - AstNode* findUnsupportedFormalRef() { - std::set constructorArgs; - std::map replacements; + void validateCovergroupExpressions() { + std::set sampleMembers; + std::set constructorRefMembers; for (AstNode* stmtp = m_constructorp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - AstVar* const varp = VN_CAST(stmtp, Var); - if (!varp || !varp->isIO()) continue; - constructorArgs.insert(varp); - if (!VN_IS(varp->dtypep()->skipRefp(), ClassRefDType)) continue; - AstVar* const memberp + const AstVar* const varp = VN_CAST(stmtp, Var); + if (!varp || !varp->isIO() || (!varp->isRef() && !varp->isConstRef())) continue; + const AstVar* const memberp = VN_CAST(m_memberMap.findMember(m_covergroupp, varp->name()), Var); UASSERT_OBJ(memberp && memberp->isClassMember(), varp, "Covergroup constructor argument missing persistent member"); - replacements.emplace(varp, memberp); + constructorRefMembers.insert(varp); + constructorRefMembers.insert(memberp); } - FormalRefVisitor visitor{constructorArgs, replacements}; - for (AstCoverpoint* const cpp : m_coverpoints) { - visitor.scan(cpp->exprp()); - visitor.scan(cpp->iffp()); + for (AstNode* stmtp = m_sampleFuncp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + const AstVar* const varp = VN_CAST(stmtp, Var); + if (!varp || !varp->isIO()) continue; + const AstVar* const memberp + = VN_CAST(m_memberMap.findMember(m_covergroupp, varp->name()), Var); + UASSERT_OBJ(memberp && memberp->isClassMember(), varp, + "Covergroup sample argument missing persistent member"); + sampleMembers.insert(memberp); } - for (AstCoverCross* const crossp : m_coverCrosses) visitor.scan(crossp->iffp()); - return visitor.offenderp(); + CovergroupExprValidVisitor{sampleMembers, constructorRefMembers}.scan(m_constructorp); + } + + void rebindFormalRefs() { + std::map replacements; + for (AstNode* stmtp = m_constructorp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (const AstVar* const varp = VN_CAST(stmtp, Var)) { + if (!varp->isIO()) continue; + AstVar* const memberp + = VN_CAST(m_memberMap.findMember(m_covergroupp, varp->name()), Var); + UASSERT_OBJ(memberp && memberp->isClassMember(), varp, + "Covergroup constructor argument missing persistent member"); + replacements.emplace(varp, memberp); + } + } + size_t expectedBindings = 0; + for (const auto& pair : replacements) { + if (pair.first->isRef() || pair.first->isConstRef()) ++expectedBindings; + } + size_t rewrittenBindings = 0; + for (AstNode* stmtp = m_constructorp->stmtsp(); stmtp;) { + AstNode* const nextp = stmtp->nextp(); + if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) { + AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef); + AstVarRef* const rhsp = VN_CAST(assignp->rhsp(), VarRef); + if (lhsp && rhsp + && (lhsp->varp()->declDirection() == VDirection::REF + || lhsp->varp()->declDirection() == VDirection::CONSTREF)) { + const auto it = replacements.find(rhsp->varp()); + UASSERT_OBJ(it != replacements.end() && it->second == lhsp->varp(), assignp, + "Unexpected covergroup reference binding assignment"); + AstCExpr* const bindp = new AstCExpr{assignp->fileline(), ""}; + bindp->add(lhsp->unlinkFrBack()); + bindp->add(" = &"); + bindp->add(rhsp->unlinkFrBack()); + assignp->replaceWith(bindp->makeStmt()); + pushDeletep(assignp); + ++rewrittenBindings; + } + } + stmtp = nextp; + } + UASSERT_OBJ(rewrittenBindings == expectedBindings, m_constructorp, + "Covergroup reference argument missing binding"); + FormalRefVisitor visitor{replacements}; + for (AstCoverpoint* const cpp : m_coverpoints) visitor.scan(cpp); + for (AstCoverCross* const crossp : m_coverCrosses) visitor.scan(crossp); } AstVarRef* installEnclosingBackPointer(const std::vector& constructps) { @@ -1952,6 +2085,14 @@ class FunctionalCoverageVisitor final : public VNVisitor { itemp = nextp; continue; } + if (hasInstanceMemberEventRef(cgp)) { + cgp->v3warn(COVERIGN, "Unsupported: 'covergroup' clocking event " + "on member variable"); + hasUnsupportedEvent = true; + VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp); + itemp = nextp; + continue; + } // V3Active handles events that do not depend on an enclosing instance. UINFO(4, "Keeping covergroup event node for V3Active: " << nodep->name()); itemp = nextp; @@ -1960,14 +2101,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { itemp = nextp; } - // If covergroup has unsupported clocking event, skip processing it - // but still clean up coverpoints so they don't reach downstream passes - if (hasUnsupportedEvent) { - iterateChildren(nodep); - deleteCoverageItems(); - return; - } - // Find the sample() method and constructor m_sampleFuncp = VN_CAST(m_memberMap.findMember(nodep, "sample"), Func); // V3LinkParse always synthesizes a sample() method for every covergroup, and the @@ -1978,12 +2111,12 @@ class FunctionalCoverageVisitor final : public VNVisitor { UINFO(9, "Found sample() method: " << (m_sampleFuncp ? "yes" : "no")); UINFO(9, "Found constructor: " << (m_constructorp ? "yes" : "no")); - iterateChildren(nodep); - - if (AstNode* const offenderp = findUnsupportedFormalRef()) { - offenderp->v3warn(COVERIGN, "Unsupported: 'covergroup' coverpoint dereferencing a " - "non-class constructor argument; ignoring covergroup " - << nodep->prettyNameQ()); + // If covergroup has unsupported clocking event, skip processing it + // but still clean up coverpoints so they don't reach downstream passes + if (hasUnsupportedEvent) { + iterateChildren(nodep); + validateCovergroupExpressions(); + rebindFormalRefs(); deleteCoverageItems(); if (embeddedEventForkp) { VL_DO_DANGLING(pushDeletep(embeddedEventForkp), embeddedEventForkp); @@ -1991,6 +2124,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { return; } + iterateChildren(nodep); + validateCovergroupExpressions(); + rebindFormalRefs(); const std::vector constructps = findCovergroupConstructions(); // Embedded covergroups (IEEE 1800-2023 19.4): coverpoints, iff expressions, and diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 2e93700a5..d2d88a0e6 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -185,6 +185,12 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) { const AstBasicDType* const basicp = nodep->basicp(); const bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); if (nodep->mtaskCacheLineAlign() && !asRef) putns(nodep, "alignas(VL_CACHE_LINE_BYTES) "); + if (nodep->covergroupRefMember()) { + UASSERT_OBJ(!asRef, nodep, "Covergroup reference member emitted as C++ reference"); + putns(nodep, nodep->dtypep()->cType("", false, false)); + puts(" const* " + nodep->nameProtect() + " = nullptr;\n"); + return; + } const auto emitDeclArrayBrackets = [this](const AstVar* nodep) -> void { // This isn't very robust and may need cleanup for other data types diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 5529f4b8a..4d6f62ecc 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1768,6 +1768,9 @@ public: void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); const AstNodeModule* const varModp = EmitCParentModule::get(varp); + const bool dereferenceCovergroupRef + = varp->covergroupRefMember() && nodep->access().isReadOrRW(); + if (dereferenceCovergroupRef) putns(nodep, "(*"); if (EmitCUtil::isConstPoolMod(varModp)) { // Reference to constant pool variable putns(nodep, EmitCUtil::topClassName() + "__ConstPool__"); @@ -1784,6 +1787,7 @@ public: emitDereference(nodep, nodep->selfPointerProtect(m_useSelfForThis)); } putns(nodep, nodep->varp()->nameProtect()); + if (dereferenceCovergroupRef) puts(")"); } void visit(AstAddrOfCFunc* nodep) override { // Note: Can be thought to handle more, but this is all that is needed right now diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 862e28379..888f0e6bf 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -118,7 +118,7 @@ class EmitCHeader final : public EmitCConstInit { // Leftovers, just in case off by one error somewhere above for (; it != varList.cend(); ++it) emitVarDecl(*it); } else { // Output as nonanons - for (const auto& pair : varList) emitVarDecl(pair); + for (const AstVar* const varp : varList) emitVarDecl(varp); } varList.clear(); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 36f6fab19..92f3aaf40 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2811,6 +2811,10 @@ private: // Remember the alias - can't do it yet because we may have additional symbols to be added, // or maybe an alias of an alias m_statep->insertScopeAlias(LinkDotState::SAMN_IFTOP, lhsSymp, rhsSymp); + AstVarScope* const lhsVscp = VN_CAST(lhsSymp->nodep(), VarScope); + AstVarScope* const rhsVscp = VN_CAST(rhsSymp->nodep(), VarScope); + UASSERT_OBJ(lhsVscp && rhsVscp, nodep, "Interface alias missing variable scope"); + setAliasVarScope(lhsVscp, rhsVscp); // We have stored the link, we don't need these any more VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } @@ -5186,7 +5190,7 @@ class LinkDotResolveVisitor final : public VNVisitor { checkNoDot(nodep); iterateChildren(nodep); AstVarScope* aliasp = LinkDotScopeVisitor::getAliasVarScopep(nodep); - if (aliasp && aliasp != nodep) { + if (aliasp && aliasp != nodep && !nodep->varp()->isIfaceRef()) { // Aliased variable might still be references from outside, // eg through the VPI, and is traced, so we need the value to propagate. // TODO: this means external writes to the LHS (e.g.: through the VPI) don't work diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 86569766a..efc33edee 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -345,6 +345,11 @@ class LinkParseVisitor final : public VNVisitor { void visit(AstVar* nodep) override { cleanFileline(nodep); UINFO(9, "VAR " << nodep); + const AstClass* const classp = VN_CAST(m_modp, Class); + if (classp && classp->isCovergroup() && nodep->isClassMember() && !nodep->isFuncLocal() + && (nodep->declDirection().isRef() || nodep->declDirection().isConstRef())) { + nodep->covergroupRefMember(); + } if (nodep->valuep()) nodep->hasUserInit(true); // IEEE 1800-2023 6.21: for loop variables are automatic. verilog.y is // responsible for marking those. @@ -1145,17 +1150,18 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } - // Append, for each arg in argsp, an INPUT parameter plus a "this. = " + // Append, for each arg in argsp, a parameter plus a "this. = " // assignment to funcp. The parameter is a clone of the covergroup member and so shares its // name; 'this.' on the LHS targets the member, otherwise the same-named local parameter // shadows it and the assignment self-assigns the parameter, leaving the member unwritten. // argsp may be null (no args appended). - static void addArgMemberCopies(AstFunc* funcp, AstNode* argsp) { + static void addArgMemberCopies(AstFunc* funcp, AstNode* argsp, bool readOnlyRefs) { for (AstNode* argp = argsp; argp; argp = argp->nextp()) { AstVar* const origVarp = VN_AS(argp, Var); AstVar* const paramp = origVarp->cloneTree(false); paramp->funcLocal(true); - paramp->direction(VDirection::INPUT); + paramp->direction(origVarp->direction()); + if (readOnlyRefs && origVarp->isRef()) paramp->direction(VDirection::CONSTREF); funcp->addStmtsp(paramp); AstNodeExpr* const lhsp = new AstDot{ origVarp->fileline(), false, new AstParseRef{origVarp->fileline(), "this"}, @@ -1184,7 +1190,7 @@ class LinkParseVisitor final : public VNVisitor { // before the coverage body, then re-append the body. AstNode* const existingBodyp = newFuncp->stmtsp(); if (existingBodyp) existingBodyp->unlinkFrBackWithNext(); - addArgMemberCopies(newFuncp, argsp); + addArgMemberCopies(newFuncp, argsp, true); if (existingBodyp) newFuncp->addStmtsp(existingBodyp); } @@ -1213,7 +1219,7 @@ class LinkParseVisitor final : public VNVisitor { // IEEE: function void sample([arguments]) { AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr}; - addArgMemberCopies(funcp, sampleArgsp); + addArgMemberCopies(funcp, sampleArgsp, false); funcp->classMethod(true); funcp->dtypep(funcp->findVoidDType()); nodep->addMembersp(funcp); @@ -1299,16 +1305,33 @@ class LinkParseVisitor final : public VNVisitor { // Convert constructor args to member variables for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) { AstVar* const origVarp = VN_AS(argp, Var); + if (origVarp->direction() == VDirection::OUTPUT + || origVarp->direction() == VDirection::INOUT) { + origVarp->v3error("Covergroup formal arguments cannot be output or inout" + " (IEEE 1800-2012 19.3)"); + origVarp->direction(VDirection::INPUT); + } + if ((origVarp->isRef() || origVarp->isConstRef()) && origVarp->valuep()) { + origVarp->v3warn(E_UNSUPPORTED, + "Unsupported: default value on ref or const ref covergroup " + "formal argument"); + } AstVar* const memberp = origVarp->cloneTree(false); memberp->varType(VVarType::MEMBER); memberp->funcLocal(false); memberp->direction(VDirection::NONE); + if (origVarp->isRef() || origVarp->isConstRef()) memberp->noReset(true); cgClassp->addMembersp(memberp); } // Convert sample args to member variables for (AstNode* argp = nodep->sampleArgsp(); argp; argp = argp->nextp()) { AstVar* const origVarp = VN_AS(argp, Var); + if (!origVarp->isInput()) { + origVarp->v3error("Covergroup sample formal argument must have input direction " + "(IEEE 1800-2012 19.8.1)."); + origVarp->direction(VDirection::INPUT); + } AstVar* const memberp = origVarp->cloneTree(false); memberp->varType(VVarType::MEMBER); memberp->funcLocal(false); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f4df73a2c..ab9541888 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -7566,12 +7566,34 @@ class WidthVisitor final : public VNVisitor { if (!pinp) continue; // Argument error we'll find later AstNodeDType* const portDTypep = portp->dtypep()->skipRefToEnump(); const AstNodeDType* const pinDTypep = pinp->dtypep()->skipRefToEnump(); - if (portp->direction() == VDirection::REF - && !similarDTypeRecurse(portDTypep, pinDTypep)) { - pinp->v3error("Ref argument requires matching types;" - << " port " << portp->prettyNameQ() << " requires " - << portDTypep->prettyDTypeNameQ() << " but connection is " - << pinDTypep->prettyDTypeNameQ() << "."); + const AstIfaceRefDType* const portIfacep + = VN_CAST(portDTypep->elemDTypep(true), IfaceRefDType); + const VCastable ifaceCastable + = portIfacep ? AstNode::computeCastable(portDTypep, pinDTypep, pinp, + /* checkIfaceArgCompat */ true) + : VCastable{VCastable::UNSUPPORTED}; + const bool matchingRefDTypes = portIfacep + ? ifaceCastable == VCastable::SAMEISH + : similarDTypeRecurse(portDTypep, pinDTypep); + const bool pinIsNull = VN_IS(pinp, Const) && VN_AS(pinp, Const)->num().isNull(); + if ((portp->isRef() || portp->isConstRef()) && !matchingRefDTypes) { + if (portIfacep) { + pinp->v3error("Ref virtual interface argument " + << portp->prettyNameQ() + << " requires the same interface type, parameters, and " + "modport."); + } else { + pinp->v3error("Ref argument requires matching types;" + << " port " << portp->prettyNameQ() << " requires " + << portDTypep->prettyDTypeNameQ() << " but connection is " + << pinDTypep->prettyDTypeNameQ() << "."); + } + } else if (portIfacep && portIfacep->isVirtual() && portp->isInput() && !pinIsNull + && !ifaceCastable.isAssignable()) { + pinp->v3error("Virtual interface argument " + << portp->prettyNameQ() + << " requires a compatible interface type, parameters, and " + "modport."); } else if (portp->isWritable() && pinp->width() != portp->width()) { pinp->v3widthWarn(portp->width(), pinp->width(), "Function output argument " diff --git a/test_regress/t/t_castdyn_unsup_bad.out b/test_regress/t/t_castdyn_unsup_bad.out index 5bda83857..d0c896177 100644 --- a/test_regress/t/t_castdyn_unsup_bad.out +++ b/test_regress/t/t_castdyn_unsup_bad.out @@ -1,7 +1,17 @@ -%Error-UNSUPPORTED: t/t_castdyn_unsup_bad.v:13:5: Unsupported: $cast to 'string$[$]' from 'int$[string]' +%Error-UNSUPPORTED: t/t_castdyn_unsup_bad.v:22:5: Unsupported: $cast to 'string$[$]' from 'int$[string]' : ... note: In instance 't' : ... Suggest try static cast - 13 | $cast(q, aarray); + 22 | $cast(q, aarray); | ^~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_castdyn_unsup_bad.v:23:5: Unsupported: $cast to 'IFACEREFDTYPE' from 'IFACEREFDTYPE' + : ... note: In instance 't' + : ... Suggest try static cast + 23 | $cast(monitored, intf); + | ^~~~~ +%Error-UNSUPPORTED: t/t_castdyn_unsup_bad.v:24:5: Unsupported: $cast to 'IFACEREFDTYPE$[0:1]' from 'IFACEREFDTYPE$[0:1]' + : ... note: In instance 't' + : ... Suggest try static cast + 24 | $cast(vifaces, ifaces); + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_castdyn_unsup_bad.v b/test_regress/t/t_castdyn_unsup_bad.v index f21d7b742..aeee61193 100644 --- a/test_regress/t/t_castdyn_unsup_bad.v +++ b/test_regress/t/t_castdyn_unsup_bad.v @@ -4,13 +4,24 @@ // SPDX-FileCopyrightText: 2020 Wilson Snyder // SPDX-License-Identifier: CC0-1.0 +interface Iface; + logic value; + modport monitor(input value); +endinterface + module t; string q[$]; int aarray[string]; + Iface intf(); + Iface ifaces[2] (); + virtual Iface.monitor monitored; + virtual Iface vifaces[2]; initial begin $cast(q, aarray); + $cast(monitored, intf); + $cast(vifaces, ifaces); end endmodule diff --git a/test_regress/t/t_covergroup_args.out b/test_regress/t/t_covergroup_args.out index 69ff229d6..2c5b46bd0 100644 --- a/test_regress/t/t_covergroup_args.out +++ b/test_regress/t/t_covergroup_args.out @@ -1,8 +1,152 @@ -__vlAnonCG_cg.__Vcoverpoint0.auto_0: 1 -__vlAnonCG_cg.__Vcoverpoint0.auto_1: 0 +__vlAnonCG_cg.cp_iff.one: 1 +__vlAnonCG_cg.cp_test.one: 1 +__vlAnonCG_cg.cp_test.zero: 1 +__vlAnonCG_explicit_cg.cp_explicit.one: 1 +__vlAnonCG_explicit_cg.cp_explicit.zero: 1 +__vlAnonCG_mixed_cg.cp_sum.three: 1 +__vlAnonCG_mixed_cg.cp_sum.two: 1 +__vlAnonCG_parent_cg.cp_parent.one: 1 +__vlAnonCG_parent_cg.cp_parent.zero: 1 +__vlAnonCG_parent_ref_cg.cp_parent_ref.one: 1 +__vlAnonCG_parent_ref_cg.cp_parent_ref.zero: 1 cg.cp1.hi: 0 cg.cp1.lo: 1 cg_clocked.cp_clocked.hi: 0 cg_clocked.cp_clocked.lo: 1 +cg_const_ref_handle.cp_const_ref_handle.one: 1 +cg_const_ref_handle.cp_const_ref_handle.zero: 1 +cg_const_ref_scalar.cp_const_ref_scalar.one: 1 +cg_const_ref_scalar.cp_const_ref_scalar.zero: 1 +cg_cross_iff.cp_a.one: 2 +cg_cross_iff.cp_b.one: 2 +cg_cross_iff.cross_ab.one_x_one [cross]: 1 +cg_global.cp_global.one: 1 +cg_global.cp_global.zero: 2 +cg_interface_arrays.cp_const_ref.one: 0 +cg_interface_arrays.cp_const_ref.zero: 2 +cg_interface_arrays.cp_input.one: 0 +cg_interface_arrays.cp_input.zero: 2 +cg_interface_arrays.cp_ref.one: 0 +cg_interface_arrays.cp_ref.zero: 2 +cg_interface_arrays.cp_ref_2d.one: 0 +cg_interface_arrays.cp_ref_2d.zero: 2 +cg_interfaces.cp_basic_input.one: 1 +cg_interfaces.cp_basic_input.zero: 2 +cg_interfaces.cp_param_const_ref.one: 2 +cg_interfaces.cp_param_const_ref.zero: 1 +cg_interfaces.cp_param_ref.one: 2 +cg_interfaces.cp_param_ref.zero: 1 +cg_interfaces_unqualified.cp_basic.one: 1 +cg_interfaces_unqualified.cp_basic.zero: 2 +cg_interfaces_unqualified.cp_param.one: 0 +cg_interfaces_unqualified.cp_param.zero: 3 +cg_mixed_refs.cp_mixed_refs.one: 2 +cg_mixed_refs.cp_mixed_refs.two: 1 +cg_multiple.cp_first.one: 2 +cg_multiple.cp_first.zero: 2 +cg_multiple.cp_second.one: 2 +cg_multiple.cp_second.zero: 2 +cg_null_interface.cp.auto_0: 0 +cg_null_interface.cp.auto_1: 0 +cg_null_interface.cp.auto_10: 0 +cg_null_interface.cp.auto_11: 0 +cg_null_interface.cp.auto_12: 0 +cg_null_interface.cp.auto_13: 0 +cg_null_interface.cp.auto_14: 0 +cg_null_interface.cp.auto_15: 0 +cg_null_interface.cp.auto_16: 0 +cg_null_interface.cp.auto_17: 0 +cg_null_interface.cp.auto_18: 0 +cg_null_interface.cp.auto_19: 0 +cg_null_interface.cp.auto_2: 0 +cg_null_interface.cp.auto_20: 0 +cg_null_interface.cp.auto_21: 0 +cg_null_interface.cp.auto_22: 0 +cg_null_interface.cp.auto_23: 0 +cg_null_interface.cp.auto_24: 0 +cg_null_interface.cp.auto_25: 0 +cg_null_interface.cp.auto_26: 0 +cg_null_interface.cp.auto_27: 0 +cg_null_interface.cp.auto_28: 0 +cg_null_interface.cp.auto_29: 0 +cg_null_interface.cp.auto_3: 0 +cg_null_interface.cp.auto_30: 0 +cg_null_interface.cp.auto_31: 0 +cg_null_interface.cp.auto_32: 0 +cg_null_interface.cp.auto_33: 0 +cg_null_interface.cp.auto_34: 0 +cg_null_interface.cp.auto_35: 0 +cg_null_interface.cp.auto_36: 0 +cg_null_interface.cp.auto_37: 0 +cg_null_interface.cp.auto_38: 0 +cg_null_interface.cp.auto_39: 0 +cg_null_interface.cp.auto_4: 0 +cg_null_interface.cp.auto_40: 0 +cg_null_interface.cp.auto_41: 0 +cg_null_interface.cp.auto_42: 0 +cg_null_interface.cp.auto_43: 0 +cg_null_interface.cp.auto_44: 0 +cg_null_interface.cp.auto_45: 0 +cg_null_interface.cp.auto_46: 0 +cg_null_interface.cp.auto_47: 0 +cg_null_interface.cp.auto_48: 0 +cg_null_interface.cp.auto_49: 0 +cg_null_interface.cp.auto_5: 0 +cg_null_interface.cp.auto_50: 0 +cg_null_interface.cp.auto_51: 0 +cg_null_interface.cp.auto_52: 0 +cg_null_interface.cp.auto_53: 0 +cg_null_interface.cp.auto_54: 0 +cg_null_interface.cp.auto_55: 0 +cg_null_interface.cp.auto_56: 0 +cg_null_interface.cp.auto_57: 0 +cg_null_interface.cp.auto_58: 0 +cg_null_interface.cp.auto_59: 0 +cg_null_interface.cp.auto_6: 0 +cg_null_interface.cp.auto_60: 0 +cg_null_interface.cp.auto_61: 0 +cg_null_interface.cp.auto_62: 0 +cg_null_interface.cp.auto_63: 0 +cg_null_interface.cp.auto_7: 0 +cg_null_interface.cp.auto_8: 0 +cg_null_interface.cp.auto_9: 0 +cg_param_class.cp_param_const_ref.one: 2 +cg_param_class.cp_param_const_ref.zero: 1 +cg_param_class.cp_param_input.one: 1 +cg_param_class.cp_param_input.zero: 2 +cg_param_class.cp_param_ref.one: 2 +cg_param_class.cp_param_ref.zero: 1 +cg_port.cp_basic_qualified.one: 0 +cg_port.cp_basic_qualified.zero: 1 +cg_port.cp_basic_unqualified.one: 0 +cg_port.cp_basic_unqualified.zero: 1 +cg_port.cp_param_qualified.one: 0 +cg_port.cp_param_qualified.zero: 1 +cg_port.cp_param_unqualified.one: 0 +cg_port.cp_param_unqualified.zero: 1 +cg_ref_handle.cp_ref_handle.one: 1 +cg_ref_handle.cp_ref_handle.zero: 1 +cg_ref_scalar.cp_ref_scalar.one: 1 +cg_ref_scalar.cp_ref_scalar.zero: 1 +cg_ref_types.cp_array.one: 1 +cg_ref_types.cp_array.zero: 1 +cg_ref_types.cp_handle_compare.isnull: 1 +cg_ref_types.cp_handle_compare.nonnull: 1 +cg_ref_types.cp_struct.one: 1 +cg_ref_types.cp_struct.zero: 1 +cg_ref_types.cp_wide.one: 1 +cg_ref_types.cp_wide.zero: 1 +cg_ref_types.cp_wide_helper.one: 1 +cg_ref_types.cp_wide_helper.zero: 1 cg_samp.cp.b0: 1 cg_samp.cp.b3: 1 +cg_sample_inputs.cp_sample_inputs.one: 1 +cg_sample_inputs.cp_sample_inputs.zero: 1 +cg_sample_interfaces.cp_basic_input.one: 1 +cg_sample_interfaces.cp_basic_input.zero: 1 +cg_sample_interfaces.cp_param_input.one: 1 +cg_sample_interfaces.cp_param_input.zero: 1 +cg_sample_param_class.cp_first.one: 1 +cg_sample_param_class.cp_first.zero: 1 +cg_sample_param_class.cp_second.one: 1 +cg_sample_param_class.cp_second.zero: 1 diff --git a/test_regress/t/t_covergroup_args.v b/test_regress/t/t_covergroup_args.v index 6837fb0af..2ef6138f8 100644 --- a/test_regress/t/t_covergroup_args.v +++ b/test_regress/t/t_covergroup_args.v @@ -11,11 +11,72 @@ endclass class CoverageState; bit test; + bit enable; endclass +class ParameterizedCoverageState #(int WIDTH = 5); + bit [WIDTH-1:0] value; + bit enable; +endclass + +interface BasicCoverageIf; + logic value; + logic enable; + modport monitor(input value, enable); +endinterface + +interface ParameterizedCoverageIf #(int WIDTH = 5); + logic [WIDTH-1:0] value; + logic enable; + modport monitor(input value, enable); +endinterface + +module CoverageInterfacePort(BasicCoverageIf basic_port, + ParameterizedCoverageIf param_port); + covergroup cg_port(input virtual BasicCoverageIf basic_unqualified, + input virtual BasicCoverageIf.monitor basic_qualified, + input virtual ParameterizedCoverageIf #(5) param_unqualified, + input virtual ParameterizedCoverageIf #(5).monitor param_qualified); + cp_basic_unqualified: coverpoint basic_unqualified.value { + bins zero = {0}; + bins one = {1}; + } + cp_basic_qualified: coverpoint basic_qualified.value { + bins zero = {0}; + bins one = {1}; + } + cp_param_unqualified: coverpoint param_unqualified.value { + bins zero = {0}; + bins one = {1}; + } + cp_param_qualified: coverpoint param_qualified.value { + bins zero = {0}; + bins one = {1}; + } + endgroup + + cg_port cov; + + initial begin + cov = new(basic_port, basic_port, param_port, param_port); + cov.sample(); + end +endmodule + +typedef struct packed { + bit flag; + bit [2:0] value; +} PackedCoverageState; + class Coverage; covergroup cg(CoverageState st); - coverpoint st.test; + cp_test: coverpoint st.test { + bins zero = {0}; + bins one = {1}; + } + cp_iff: coverpoint st.test iff (st.enable) { + bins one = {1}; + } endgroup CoverageState state; function new(); @@ -24,6 +85,70 @@ class Coverage; endfunction endclass +class CoverageExplicitInput; + covergroup explicit_cg(input CoverageState st); + cp_explicit: coverpoint st.test { + bins zero = {0}; + bins one = {1}; + } + endgroup + CoverageState state; + function new(); + state = new(); + explicit_cg = new(state); + endfunction +endclass + +class CoverageParent; + CoverageState state; + function new(); + state = new(); + endfunction +endclass + +class CoverageFromParent extends CoverageParent; + covergroup parent_cg(CoverageState st); + cp_parent: coverpoint st.test { + bins zero = {0}; + bins one = {1}; + } + endgroup + function new(); + super.new(); + parent_cg = new(state); + endfunction +endclass + +class CoverageRefFromParent extends CoverageParent; + covergroup parent_ref_cg(ref CoverageState st); + cp_parent_ref: coverpoint st.test iff (st.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + function new(); + super.new(); + state.enable = 1; + parent_ref_cg = new(state); + endfunction +endclass + +class CoverageMixed; + bit [3:0] local_value; + CoverageState state; + covergroup mixed_cg(CoverageState st); + cp_sum: coverpoint local_value + st.test { + bins two = {2}; + bins three = {3}; + } + endgroup + function new(); + local_value = 2; + state = new(); + mixed_cg = new(state); + endfunction +endclass + // Top-level (file-scope) covergroup declared outside any module covergroup cg_toplevel; cp_tl: coverpoint 0; @@ -31,13 +156,244 @@ endgroup module t; + BasicCoverageIf basic_if_a(); + BasicCoverageIf basic_if_b(); + BasicCoverageIf basic_if_port(); + ParameterizedCoverageIf #(5) param_if_a(); + ParameterizedCoverageIf #(5) param_if_b(); + ParameterizedCoverageIf #(5) param_if_port(); + CoverageInterfacePort interface_port_cov(basic_if_port, param_if_port); + int i, j; logic clk = 0; + CoverageState global_state = new; + CoverageState global_original = global_state; + CoverageState first_state = new; + CoverageState second_state = new; + CoverageState named_first_state = new; + CoverageState named_second_state = new; + CoverageState ref_state = new; + CoverageState const_ref_state = new; + CoverageState mixed_ref_state = new; + bit ref_value; + bit ref_enable = 1; + bit const_ref_value; + bit const_ref_enable = 1; + bit mixed_ref_value; + bit [1:0] mixed_ref_bias; + PackedCoverageState ref_struct; + bit [3:0] ref_array[2]; + bit [95:0] ref_wide; + CoverageState ref_compare_state = new; + ParameterizedCoverageState #(5) param_input_state; + ParameterizedCoverageState #(5) param_input_original; + ParameterizedCoverageState #(5) param_ref_state; + ParameterizedCoverageState #(5) param_const_ref_state; + virtual BasicCoverageIf basic_input_vif; +`ifdef VERILATOR + virtual ParameterizedCoverageIf #(5).monitor param_array_vifs[2]; + virtual ParameterizedCoverageIf #(5).monitor param_array_vifs_2d[2][3]; + virtual ParameterizedCoverageIf #(5).monitor param_array_vifs_shifted[3:2]; +`endif + virtual ParameterizedCoverageIf #(5).monitor param_ref_vif; + virtual ParameterizedCoverageIf #(5).monitor param_const_ref_vif; + + function automatic bit wide_ref_bit(const ref bit [95:0] value); + // verilator no_inline_task + return value[80]; + endfunction covergroup cg(int var1, int var2 = 42); cp1: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; } endgroup + covergroup cg_global(CoverageState st); + cp_global: coverpoint st.test { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_multiple(CoverageState first, CoverageState second); + cp_first: coverpoint first.test { + bins zero = {0}; + bins one = {1}; + } + cp_second: coverpoint second.test { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_ref_scalar(ref bit value, ref bit enabled); + cp_ref_scalar: coverpoint value iff (enabled) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_const_ref_scalar(const ref bit value, const ref bit enabled); + cp_const_ref_scalar: coverpoint value iff (enabled) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_ref_handle(ref CoverageState st); + cp_ref_handle: coverpoint st.test iff (st.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_const_ref_handle(const ref CoverageState st); + cp_const_ref_handle: coverpoint st.test iff (st.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_mixed_refs(input bit [1:0] bias, ref bit value, const ref CoverageState st); + cp_mixed_refs: coverpoint bias + value iff (st.enable) { + bins one = {1}; + bins two = {2}; + } + endgroup + + covergroup cg_ref_types(ref PackedCoverageState state, ref bit [3:0] values[2], + ref bit [95:0] wide_value, const ref CoverageState handle); + cp_struct: coverpoint state.value { + bins zero = {0}; + bins one = {1}; + } + cp_array: coverpoint values[1] { + bins zero = {0}; + bins one = {1}; + } + cp_wide: coverpoint wide_value[80] { + bins zero = {0}; + bins one = {1}; + } + cp_wide_helper: coverpoint wide_ref_bit(wide_value) { + bins zero = {0}; + bins one = {1}; + } + cp_handle_compare: coverpoint (handle == null) { + bins nonnull = {0}; + bins isnull = {1}; + } + endgroup + + covergroup cg_param_class(input ParameterizedCoverageState #(5) input_state, + ref ParameterizedCoverageState #(5) ref_state, + const ref ParameterizedCoverageState #(5) const_ref_state); + cp_param_input: coverpoint input_state.value { + bins zero = {0}; + bins one = {1}; + } + cp_param_ref: coverpoint ref_state.value iff (ref_state.enable) { + bins zero = {0}; + bins one = {1}; + } + cp_param_const_ref: coverpoint const_ref_state.value iff (const_ref_state.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_interfaces(input virtual BasicCoverageIf.monitor input_vif, + ref virtual ParameterizedCoverageIf #(5).monitor ref_vif, + const ref virtual ParameterizedCoverageIf #(5).monitor const_ref_vif); + cp_basic_input: coverpoint input_vif.value iff (input_vif.enable) { + bins zero = {0}; + bins one = {1}; + } + cp_param_ref: coverpoint ref_vif.value iff (ref_vif.enable) { + bins zero = {0}; + bins one = {1}; + } + cp_param_const_ref: coverpoint const_ref_vif.value iff (const_ref_vif.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_interfaces_unqualified(input virtual BasicCoverageIf basic_vif, + input virtual ParameterizedCoverageIf #(5) param_vif); + cp_basic: coverpoint basic_vif.value { + bins zero = {0}; + bins one = {1}; + } + cp_param: coverpoint param_vif.value { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_null_interface(input virtual BasicCoverageIf vif); + cp: coverpoint 0; + endgroup + +`ifdef VERILATOR + // Commercial simulator support is mixed, some report virtual-interface array covergroup + // arguments as not yet implemented and Questa crashes. + covergroup cg_interface_arrays( + input virtual ParameterizedCoverageIf #(5).monitor input_vifs[2], + ref virtual ParameterizedCoverageIf #(5).monitor ref_vifs[2], + const ref virtual ParameterizedCoverageIf #(5).monitor const_ref_vifs[2], + ref virtual ParameterizedCoverageIf #(5).monitor ref_vifs_2d[2][3]); + cp_input: coverpoint input_vifs[0].value { + bins zero = {0}; + bins one = {1}; + } + cp_ref: coverpoint ref_vifs[0].value { + bins zero = {0}; + bins one = {1}; + } + cp_const_ref: coverpoint const_ref_vifs[0].value { + bins zero = {0}; + bins one = {1}; + } + cp_ref_2d: coverpoint ref_vifs_2d[0][0].value { + bins zero = {0}; + bins one = {1}; + } + endgroup +`endif + + covergroup cg_sample_inputs with function sample(input bit value, input CoverageState st); + cp_sample_inputs: coverpoint value iff (st.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_sample_param_class with function sample( + input ParameterizedCoverageState #(5) first_state, + input ParameterizedCoverageState #(5) second_state); + cp_first: coverpoint first_state.value iff (first_state.enable) { + bins zero = {0}; + bins one = {1}; + } + cp_second: coverpoint second_state.value iff (second_state.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + + covergroup cg_sample_interfaces with function sample( + input virtual BasicCoverageIf basic_vif, + input virtual ParameterizedCoverageIf #(5).monitor param_vif); + cp_basic_input: coverpoint basic_vif.value iff (basic_vif.enable) { + bins zero = {0}; + bins one = {1}; + } + cp_param_input: coverpoint param_vif.value iff (param_vif.enable) { + bins zero = {0}; + bins one = {1}; + } + endgroup + // Clocked covergroup with constructor arguments covergroup cg_clocked(int lim) @(posedge clk); cp_clocked: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; } @@ -50,13 +406,56 @@ module t; cp: coverpoint x { bins b0 = {0}; bins b3 = {3}; } endgroup + covergroup cg_cross_iff with function sample(bit a, bit b, bit enable_a, bit enable_b); + cp_a: coverpoint a iff (enable_a) { bins one = {1}; } + cp_b: coverpoint b iff (enable_b) { bins one = {1}; } + cross_ab: cross cp_a, cp_b; + endgroup + cg cov1 = new(69, 77); cg cov2 = new(69); cg_clocked cov_clocked = new(10); cg_samp cov_samp = new; - Coverage cov = new; + cg_cross_iff cov_cross_iff = new; + Coverage cov_handle = new; + CoverageExplicitInput cov_explicit = new; + CoverageFromParent cov_parent = new; + CoverageRefFromParent cov_ref_parent = new; + CoverageMixed cov_mixed = new; + cg_global cov_global = new(global_state); + cg_multiple cov_multiple = new(first_state, second_state); + cg_multiple cov_named + = new(.second(named_second_state), .first(named_first_state)); + cg_ref_scalar cov_ref_scalar = new(ref_value, ref_enable); + cg_const_ref_scalar cov_const_ref_scalar = new(const_ref_value, const_ref_enable); + cg_ref_handle cov_ref_handle = new(ref_state); + cg_const_ref_handle cov_const_ref_handle = new(const_ref_state); + cg_mixed_refs cov_mixed_refs; + cg_ref_types cov_ref_types = new(ref_struct, ref_array, ref_wide, ref_compare_state); + cg_param_class cov_param_class; +`ifdef VERILATOR + cg_interface_arrays cov_interface_arrays; + cg_interface_arrays cov_interface_arrays_shifted; +`endif + cg_interfaces cov_interfaces; + cg_interfaces_unqualified cov_interfaces_unqualified; + cg_null_interface cov_null_interface = new(null); + cg_sample_inputs cov_sample_inputs = new; + cg_sample_param_class cov_sample_param_class = new; + cg_sample_interfaces cov_sample_interfaces = new; PlainClass plain_inst = new; // Non-covergroup class instance - must not affect covergroup coverage +`ifdef VERILATOR + function automatic void check_interface_array_args(); + cov_interface_arrays = new( + param_array_vifs, param_array_vifs, param_array_vifs, param_array_vifs_2d); + cov_interface_arrays_shifted = new( + param_array_vifs, param_array_vifs_shifted, param_array_vifs, param_array_vifs_2d); + cov_interface_arrays.sample(); + cov_interface_arrays_shifted.sample(); + endfunction +`endif + function void x(); real cov_result; cov1.set_inst_name("the_inst_name"); @@ -90,6 +489,35 @@ module t; endfunction initial begin + param_input_state = new; + param_input_original = param_input_state; + param_ref_state = new; + param_ref_state.enable = 1; + param_const_ref_state = new; + param_const_ref_state.enable = 1; + cov_param_class = new(param_input_state, param_ref_state, param_const_ref_state); + basic_input_vif = basic_if_a; + param_ref_vif = param_if_a; + param_const_ref_vif = param_if_a; +`ifdef VERILATOR + param_array_vifs[0] = param_if_a; + param_array_vifs[1] = param_if_b; + param_array_vifs_2d[0][0] = param_if_a; + param_array_vifs_shifted[3] = param_if_a; + param_array_vifs_shifted[2] = param_if_b; +`endif + basic_if_a.enable = 1; + basic_if_b.enable = 1; + param_if_a.enable = 1; + param_if_b.enable = 1; + cov_interfaces = new(basic_input_vif, param_ref_vif, param_const_ref_vif); + cov_interfaces_unqualified = new(basic_if_a, param_if_a); +`ifdef VERILATOR + check_interface_array_args(); +`endif + mixed_ref_bias = 1; + cov_mixed_refs + = new(.st(mixed_ref_state), .value(mixed_ref_value), .bias(mixed_ref_bias)); i = 3; x(); // samples cov1 with i=3 -> lo bin hit clk = 1; // posedge: samples cov_clocked with i=3 -> lo bin hit @@ -98,7 +526,117 @@ module t; // default 0) b3 would never be hit. cov_samp.sample(2'd0); cov_samp.sample(2'd3); - cov.cg.sample(); + cov_cross_iff.sample(1, 1, 0, 1); + cov_cross_iff.sample(1, 1, 1, 0); + cov_cross_iff.sample(1, 1, 1, 1); + cov_handle.cg.sample(); + cov_handle.state.test = 1; + cov_handle.state.enable = 1; + cov_handle.cg.sample(); + cov_explicit.explicit_cg.sample(); + cov_explicit.state.test = 1; + cov_explicit.explicit_cg.sample(); + cov_parent.parent_cg.sample(); + cov_parent.state.test = 1; + cov_parent.parent_cg.sample(); + cov_ref_parent.parent_ref_cg.sample(); + cov_ref_parent.state = new; + cov_ref_parent.state.enable = 1; + cov_ref_parent.state.test = 1; + cov_ref_parent.parent_ref_cg.sample(); + cov_mixed.mixed_cg.sample(); + cov_mixed.state.test = 1; + cov_mixed.mixed_cg.sample(); + cov_global.sample(); + global_state = new; + global_state.test = 1; + cov_global.sample(); + global_original.test = 1; + cov_global.sample(); + second_state.test = 1; + cov_multiple.sample(); + first_state.test = 1; + second_state.test = 0; + cov_multiple.sample(); + named_second_state.test = 1; + cov_named.sample(); + named_first_state.test = 1; + named_second_state.test = 0; + cov_named.sample(); + cov_ref_scalar.sample(); + ref_value = 1; + cov_ref_scalar.sample(); + ref_enable = 0; + ref_value = 0; + cov_ref_scalar.sample(); + cov_const_ref_scalar.sample(); + const_ref_value = 1; + cov_const_ref_scalar.sample(); + const_ref_enable = 0; + const_ref_value = 0; + cov_const_ref_scalar.sample(); + ref_state.enable = 1; + cov_ref_handle.sample(); + ref_state = new; + ref_state.enable = 1; + ref_state.test = 1; + cov_ref_handle.sample(); + const_ref_state.enable = 1; + cov_const_ref_handle.sample(); + const_ref_state = new; + const_ref_state.enable = 1; + const_ref_state.test = 1; + cov_const_ref_handle.sample(); + mixed_ref_state.enable = 1; + cov_mixed_refs.sample(); + mixed_ref_bias = 0; + mixed_ref_value = 1; + cov_mixed_refs.sample(); + mixed_ref_value = 0; + cov_mixed_refs.sample(); + cov_ref_types.sample(); + ref_struct.value = 1; + ref_array[1] = 1; + ref_wide[80] = 1; + ref_compare_state = null; + cov_ref_types.sample(); + cov_param_class.sample(); + param_input_state = new; + param_input_state.value = 1; + param_ref_state = new; + param_ref_state.enable = 1; + param_ref_state.value = 1; + param_const_ref_state = new; + param_const_ref_state.enable = 1; + param_const_ref_state.value = 1; + cov_param_class.sample(); + param_input_original.value = 1; + cov_param_class.sample(); + cov_interfaces.sample(); + cov_interfaces_unqualified.sample(); + basic_input_vif = basic_if_b; + basic_if_b.value = 1; + param_ref_vif = param_if_b; + param_const_ref_vif = param_if_b; + param_if_b.value = 1; + cov_interfaces.sample(); + cov_interfaces_unqualified.sample(); + basic_if_a.value = 1; + cov_interfaces.sample(); + cov_interfaces_unqualified.sample(); + ref_compare_state = new; + ref_compare_state.enable = 1; + cov_sample_inputs.sample(ref_value, ref_compare_state); + ref_value = 1; + cov_sample_inputs.sample(ref_value, ref_compare_state); + cov_sample_param_class.sample(param_ref_state, param_const_ref_state); + param_ref_state.value = 0; + param_const_ref_state.value = 0; + cov_sample_param_class.sample(param_ref_state, param_const_ref_state); + cov_sample_interfaces.sample(basic_input_vif, param_ref_vif); + basic_if_b.value = 0; + param_if_b.value = 0; + cov_sample_interfaces.sample(basic_input_vif, param_ref_vif); $finish; end diff --git a/test_regress/t/t_covergroup_args_bad.out b/test_regress/t/t_covergroup_args_bad.out new file mode 100644 index 000000000..7c71eed2f --- /dev/null +++ b/test_regress/t/t_covergroup_args_bad.out @@ -0,0 +1,26 @@ +%Error: t/t_covergroup_args_bad.v:10:35: Covergroup formal arguments cannot be output or inout (IEEE 1800-2012 19.3) + 10 | covergroup cg_output(output bit arg); + | ^~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_args_bad.v:14:33: Covergroup formal arguments cannot be output or inout (IEEE 1800-2012 19.3) + 14 | covergroup cg_inout(inout bit arg); + | ^~~ +%Error: t/t_covergroup_args_bad.v:18:63: Covergroup sample formal argument must have input direction (IEEE 1800-2012 19.8.1). + 18 | covergroup cg_sample_output with function sample(output bit arg); + | ^~~ +%Error: t/t_covergroup_args_bad.v:22:61: Covergroup sample formal argument must have input direction (IEEE 1800-2012 19.8.1). + 22 | covergroup cg_sample_inout with function sample(inout bit arg); + | ^~~ +%Error: t/t_covergroup_args_bad.v:26:57: Covergroup sample formal argument must have input direction (IEEE 1800-2012 19.8.1). + 26 | covergroup cg_sample_ref with function sample(ref bit arg); + | ^~~ +%Error: t/t_covergroup_args_bad.v:30:69: Covergroup sample formal argument must have input direction (IEEE 1800-2012 19.8.1). + 30 | covergroup cg_sample_const_ref with function sample(const ref bit arg); + | ^~~ +%Error: t/t_covergroup_args_bad.v:34:61: Duplicate declaration of signal: 'arg' + 34 | covergroup cg_duplicate(bit arg) with function sample(bit arg); + | ^~~ + t/t_covergroup_args_bad.v:34:31: ... Location of original declaration + 34 | covergroup cg_duplicate(bit arg) with function sample(bit arg); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_embedded_unsup.py b/test_regress/t/t_covergroup_args_bad.py similarity index 100% rename from test_regress/t/t_covergroup_embedded_unsup.py rename to test_regress/t/t_covergroup_args_bad.py diff --git a/test_regress/t/t_covergroup_args_bad.v b/test_regress/t/t_covergroup_args_bad.v new file mode 100644 index 000000000..60ec5ba7b --- /dev/null +++ b/test_regress/t/t_covergroup_args_bad.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + bit value; + + covergroup cg_output(output bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_inout(inout bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_sample_output with function sample(output bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_sample_inout with function sample(inout bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_sample_ref with function sample(ref bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_sample_const_ref with function sample(const ref bit arg); + cp: coverpoint arg; + endgroup + + covergroup cg_duplicate(bit arg) with function sample(bit arg); + cp: coverpoint arg; + endgroup + + cg_output output_cg = new(value); + cg_inout inout_cg = new(value); + cg_sample_output sample_output_cg = new; + cg_sample_inout sample_inout_cg = new; + cg_sample_ref sample_ref_cg = new; + cg_sample_const_ref sample_const_ref_cg = new; + cg_duplicate duplicate_cg = new(value); +endmodule diff --git a/test_regress/t/t_covergroup_args_expr_bad.out b/test_regress/t/t_covergroup_args_expr_bad.out new file mode 100644 index 000000000..92636d847 --- /dev/null +++ b/test_regress/t/t_covergroup_args_expr_bad.out @@ -0,0 +1,48 @@ +%Error: t/t_covergroup_args_expr_bad.v:19:26: Function 'function_ref' called in a covergroup expression has an output, inout, or non-const ref argument (IEEE 1800-2012 19.5). + : ... note: In instance 't' + 19 | bins ref_bin = {0, function_ref(value)}; + | ^~~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_args_expr_bad.v:19:39: Covergroup sample formal argument 'value' may only be used in a coverpoint or conditional guard expression (IEEE 1800-2012 19.8.1). + : ... note: In instance 't' + 19 | bins ref_bin = {0, function_ref(value)}; + | ^~~~~ +%Error: t/t_covergroup_args_expr_bad.v:20:26: Function 'function_output' called in a covergroup expression has an output, inout, or non-const ref argument (IEEE 1800-2012 19.5). + : ... note: In instance 't' + 20 | bins output_bin = {function_output(value)}; + | ^~~~~~~~~~~~~~~ +%Error: t/t_covergroup_args_expr_bad.v:20:42: Covergroup sample formal argument 'value' may only be used in a coverpoint or conditional guard expression (IEEE 1800-2012 19.8.1). + : ... note: In instance 't' + 20 | bins output_bin = {function_output(value)}; + | ^~~~~ +%Error: t/t_covergroup_args_expr_bad.v:21:25: Covergroup sample formal argument 'value' may only be used in a coverpoint or conditional guard expression (IEEE 1800-2012 19.8.1). + : ... note: In instance 't' + 21 | option.at_least = value; + | ^~~~~ +%Warning-COVERIGN: t/t_covergroup_args_expr_bad.v:21:25: Ignoring unsupported: non-constant 'option.at_least'; using default value + : ... note: In instance 't' + 21 | option.at_least = value; + | ^~~~~ + ... 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. +%Error: t/t_covergroup_args_expr_bad.v:19:26: Non-constant expression in bin range; values must be constants + : ... note: In instance 't' + 19 | bins ref_bin = {0, function_ref(value)}; + | ^~~~~~~~~~~~ +%Error: t/t_covergroup_args_expr_bad.v:20:26: Non-constant expression in bin range; values must be constants + : ... note: In instance 't' + 20 | bins output_bin = {function_output(value)}; + | ^~~~~~~~~~~~~~~ +%Error: t/t_covergroup_args_expr_bad.v:27:19: Ref covergroup constructor formal argument 'value' may not be used in a covergroup expression (IEEE 1800-2012 19.5). + : ... note: In instance 't' + 27 | bins bad = {value}; + | ^~~~~ +%Error: t/t_covergroup_args_expr_bad.v:27:19: Non-constant expression in bin range; values must be constants + : ... note: In instance 't' + 27 | bins bad = {value}; + | ^~~~~ +%Error: t/t_covergroup_args_expr_bad.v:34:19: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 34 | bins bad = {[0:nonconstant_bound]}; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_args_expr_bad.py b/test_regress/t/t_covergroup_args_expr_bad.py new file mode 100755 index 000000000..77a0ac64b --- /dev/null +++ b/test_regress/t/t_covergroup_args_expr_bad.py @@ -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('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_args_expr_bad.v b/test_regress/t/t_covergroup_args_expr_bad.v new file mode 100644 index 000000000..cf7cee18a --- /dev/null +++ b/test_regress/t/t_covergroup_args_expr_bad.v @@ -0,0 +1,44 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + function automatic bit function_ref(ref bit value); + return value; + endfunction + + function automatic bit function_output(output bit value); + value = 1; + return value; + endfunction + + covergroup cg with function sample(bit value); + cp_option: coverpoint value { + bins ref_bin = {0, function_ref(value)}; + bins output_bin = {function_output(value)}; + option.at_least = value; + } + endgroup + + covergroup cg_constructor_ref(ref bit value); + cp_constructor_ref: coverpoint value { + bins bad = {value}; + } + endgroup + + bit nonconstant_bound; + covergroup cg_cross_bad; + cp_bad: coverpoint 0 iff (1) { + bins bad = {[0:nonconstant_bound]}; + } + cp_ok: coverpoint 0 { bins zero = {0}; } + cross_bad: cross cp_bad, cp_ok; + endgroup + + cg cov = new; + bit constructor_value; + cg_constructor_ref constructor_cov = new(constructor_value); + cg_cross_bad cross_bad_cov = new; +endmodule diff --git a/test_regress/t/t_covergroup_args_type_bad.out b/test_regress/t/t_covergroup_args_type_bad.out new file mode 100644 index 000000000..ed99263c7 --- /dev/null +++ b/test_regress/t/t_covergroup_args_type_bad.out @@ -0,0 +1,54 @@ +%Error: t/t_covergroup_args_type_bad.v:30:16: Virtual interface argument 'vif' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't.port_type_mismatch' + 30 | cg cov = new(intf); + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_args_type_bad.v:62:27: Assigning to const ref variable: 'value' + : ... note: In instance 't' + 62 | cp: coverpoint mutate(value); + | ^~~~~ +%Error: t/t_covergroup_args_type_bad.v:77:45: Ref argument requires matching types; port 'state' requires 'class{}BaseState' but connection is 'class{}DerivedState'. + : ... note: In instance 't' + 77 | cg_const_ref_type const_ref_type_cg = new(derived_state); + | ^~~~~~~~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:78:47: Virtual interface argument 'input_vif' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 78 | cg_interface_types interface_types_cg = new(vif6, vif6, vif6); + | ^~~~ +%Error: t/t_covergroup_args_type_bad.v:78:53: Ref virtual interface argument 'ref_vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 78 | cg_interface_types interface_types_cg = new(vif6, vif6, vif6); + | ^~~~ +%Error: t/t_covergroup_args_type_bad.v:78:59: Ref virtual interface argument 'const_ref_vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 78 | cg_interface_types interface_types_cg = new(vif6, vif6, vif6); + | ^~~~ +%Error: t/t_covergroup_args_type_bad.v:80:13: Virtual interface argument 'input_vif' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 80 | = new(monitor_vif, monitor_vif, monitor_vif); + | ^~~~~~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:80:26: Ref virtual interface argument 'ref_vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 80 | = new(monitor_vif, monitor_vif, monitor_vif); + | ^~~~~~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:80:39: Ref virtual interface argument 'const_ref_vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 80 | = new(monitor_vif, monitor_vif, monitor_vif); + | ^~~~~~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:82:42: Virtual interface argument 'vifs' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 82 | cg_array_input bad_input_modport = new(driver5); + | ^~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:83:44: Virtual interface argument 'vifs' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 83 | cg_array_input bad_input_parameter = new(monitor6); + | ^~~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:84:38: Ref virtual interface argument 'vifs' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 84 | cg_array_ref bad_ref_modport = new(driver5); + | ^~~~~~~ +%Error: t/t_covergroup_args_type_bad.v:85:52: Ref virtual interface argument 'vifs' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 85 | cg_array_const_ref bad_const_ref_parameter = new(monitor6); + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_args_type_bad.py b/test_regress/t/t_covergroup_args_type_bad.py new file mode 100755 index 000000000..77a0ac64b --- /dev/null +++ b/test_regress/t/t_covergroup_args_type_bad.py @@ -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('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_args_type_bad.v b/test_regress/t/t_covergroup_args_type_bad.v new file mode 100644 index 000000000..c6f0befbf --- /dev/null +++ b/test_regress/t/t_covergroup_args_type_bad.v @@ -0,0 +1,86 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +class BaseState; +endclass + +class DerivedState extends BaseState; +endclass + +interface ParameterizedIf #(int WIDTH = 5); + logic [WIDTH-1:0] value; + modport monitor(input value); + modport driver(output value); +endinterface + +interface ModportIf; + logic value; + modport monitor(input value); + modport driver(output value); +endinterface + +module CovergroupPortTypeMismatch(ModportIf.monitor intf); + covergroup cg(input virtual ModportIf.driver vif); + cp: coverpoint vif.value; + endgroup + + cg cov = new(intf); +endmodule + +module t; + ParameterizedIf #(5) if5(); + ParameterizedIf #(6) if6(); + ModportIf modport_if(); + CovergroupPortTypeMismatch port_type_mismatch(modport_if); + + DerivedState derived_state = new; + virtual ParameterizedIf #(5).monitor monitor_vif = if5; + virtual ParameterizedIf #(6) vif6 = if6; + virtual ParameterizedIf #(5).driver driver5[2]; + virtual ParameterizedIf #(6).monitor monitor6[2]; + bit mutable_value; + + function automatic bit mutate(ref bit value); + value = 1; + return value; + endfunction + + covergroup cg_const_ref_type(const ref BaseState state); + cp: coverpoint (state == null); + endgroup + + covergroup cg_interface_types(input virtual ParameterizedIf #(5) input_vif, + ref virtual ParameterizedIf #(5) ref_vif, + const ref virtual ParameterizedIf #(5) const_ref_vif); + cp: coverpoint input_vif.value; + endgroup + + covergroup cg_read_only_ref(ref bit value); + cp: coverpoint mutate(value); + endgroup + + covergroup cg_array_input(input virtual ParameterizedIf #(5).monitor vifs[2]); + cp: coverpoint vifs[0].value; + endgroup + + covergroup cg_array_ref(ref virtual ParameterizedIf #(5).monitor vifs[2]); + cp: coverpoint vifs[0].value; + endgroup + + covergroup cg_array_const_ref(const ref virtual ParameterizedIf #(5).monitor vifs[2]); + cp: coverpoint vifs[0].value; + endgroup + + cg_const_ref_type const_ref_type_cg = new(derived_state); + cg_interface_types interface_types_cg = new(vif6, vif6, vif6); + cg_interface_types interface_modport_types_cg + = new(monitor_vif, monitor_vif, monitor_vif); + cg_read_only_ref read_only_ref_cg = new(mutable_value); + cg_array_input bad_input_modport = new(driver5); + cg_array_input bad_input_parameter = new(monitor6); + cg_array_ref bad_ref_modport = new(driver5); + cg_array_const_ref bad_const_ref_parameter = new(monitor6); +endmodule diff --git a/test_regress/t/t_covergroup_embedded_unsup.out b/test_regress/t/t_covergroup_embedded_unsup.out deleted file mode 100644 index 18fc4eb92..000000000 --- a/test_regress/t/t_covergroup_embedded_unsup.out +++ /dev/null @@ -1,7 +0,0 @@ -%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:19:24: Unsupported: 'covergroup' coverpoint dereferencing a non-class constructor argument; ignoring covergroup '__vlAnonCG_cov_interface' - : ... note: In instance 't' - 19 | cp: coverpoint vif.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. -%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_embedded_unsup.v b/test_regress/t/t_covergroup_embedded_unsup.v deleted file mode 100644 index f2ce549b7..000000000 --- a/test_regress/t/t_covergroup_embedded_unsup.v +++ /dev/null @@ -1,34 +0,0 @@ -// 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-handle covergroup constructor arguments are rebound to persistent -// covergroup members. Virtual-interface arguments are not rebound, so -// dereferencing one after construction is unsupported and the covergroup must -// be ignored with COVERIGN. - -interface CoverageInterface; - bit test; -endinterface - -class InterfaceArgumentMonitor; - covergroup cov_interface(virtual CoverageInterface vif); - cp: coverpoint vif.test; - endgroup - - function new(virtual CoverageInterface vif); - cov_interface = new(vif); - endfunction -endclass - -module t; - CoverageInterface coverage_interface(); - InterfaceArgumentMonitor mon; - - initial begin - mon = new(coverage_interface); - end -endmodule diff --git a/test_regress/t/t_covergroup_member_event_unsup.out b/test_regress/t/t_covergroup_member_event_unsup.out index 44afaaabb..f05a52266 100644 --- a/test_regress/t/t_covergroup_member_event_unsup.out +++ b/test_regress/t/t_covergroup_member_event_unsup.out @@ -1,10 +1,22 @@ -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:15:29: Unsupported: 'covergroup' clocking event signal has no assignment within the enclosing class; no coverage sampled. Use --timing for full support. - 15 | covergroup cov_extclk @(posedge clk); +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:25:29: Unsupported: 'covergroup' clocking event signal has no assignment within the enclosing class; no coverage sampled. Use --timing for full support. + 25 | covergroup cov_extclk @(posedge clk); | ^~~~~~~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:37:5: Unsupported: 'covergroup' clocking event on complex member expression; use --timing for full support. +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:47: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); + 47 | covergroup cov_cplx @(posedge a or posedge mid.lvl.ev); | ^~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:67:3: Unsupported: 'covergroup' clocking event on member variable + : ... note: In instance 't' + 67 | covergroup cov_ref(ref logic event_ref) @(posedge event_ref); + | ^~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:71:3: Unsupported: 'covergroup' clocking event on member variable + : ... note: In instance 't' + 71 | covergroup cov_ref_select(ref logic [1:0] event_refs) @(posedge event_refs[0]); + | ^~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:75:3: Unsupported: 'covergroup' clocking event on member variable + : ... note: In instance 't' + 75 | covergroup cov_ref_vif(ref virtual EventIf event_ref_vif) @(posedge event_ref_vif.clk); + | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_member_event_unsup.py b/test_regress/t/t_covergroup_member_event_unsup.py index 99305a1c4..2a6edbb86 100755 --- a/test_regress/t/t_covergroup_member_event_unsup.py +++ b/test_regress/t/t_covergroup_member_event_unsup.py @@ -11,8 +11,13 @@ import vltest_bootstrap test.scenarios('vlt') -# Dynamic per-instance event waits require --timing. Without it, simple events use -# best-effort in-class assignment instrumentation and unsupported cases warn. +# Some cases specifically exercise the no-timing fallback: simple per-instance 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.compile( + verilator_flags2=['--Wno-COVERIGN', '--no-skip-identical', '--no-timing']) + +test.execute() + test.passes() diff --git a/test_regress/t/t_covergroup_member_event_unsup.v b/test_regress/t/t_covergroup_member_event_unsup.v index e2ff92710..69df44c79 100644 --- a/test_regress/t/t_covergroup_member_event_unsup.v +++ b/test_regress/t/t_covergroup_member_event_unsup.v @@ -7,7 +7,17 @@ // Embedded covergroup events that cannot be approximated without --timing must // emit COVERIGN rather than silently producing zero coverage. +interface EventIf; + logic clk; +endinterface + module t; + EventIf event_if(); + + logic ref_clock; + logic [1:0] ref_clocks; + virtual EventIf event_vif = event_if; + class ExternalClk; bit clk; bit [3:0] value; @@ -45,12 +55,39 @@ module t; endfunction endclass + class StaticClk; + static bit clk; + static bit value; + endclass + + covergroup cov_static @(posedge StaticClk::clk); + coverpoint StaticClk::value; + endgroup + + covergroup cov_ref(ref logic event_ref) @(posedge event_ref); + coverpoint event_ref; + endgroup + + covergroup cov_ref_select(ref logic [1:0] event_refs) @(posedge event_refs[0]); + coverpoint event_refs[0]; + endgroup + + covergroup cov_ref_vif(ref virtual EventIf event_ref_vif) @(posedge event_ref_vif.clk); + coverpoint event_ref_vif.clk; + endgroup + ExternalClk ec; ComplexEvt cx; + cov_static static_cg = new; + cov_ref ref_cg = new(ref_clock); + cov_ref_select ref_select_cg = new(ref_clocks); + cov_ref_vif ref_vif_cg = new(event_vif); initial begin ec = new; cx = new; + StaticClk::value = 1; + StaticClk::clk = 1; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_ref_default_unsup.out b/test_regress/t/t_covergroup_ref_default_unsup.out new file mode 100644 index 000000000..8d569c7ee --- /dev/null +++ b/test_regress/t/t_covergroup_ref_default_unsup.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_covergroup_ref_default_unsup.v:10:29: Unsupported: default value on ref or const ref covergroup formal argument + 10 | covergroup cg_ref(ref bit arg = value); + | ^~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_covergroup_ref_default_unsup.v:14:41: Unsupported: default value on ref or const ref covergroup formal argument + 14 | covergroup cg_const_ref(const ref bit arg = value); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_ref_default_unsup.py b/test_regress/t/t_covergroup_ref_default_unsup.py new file mode 100755 index 000000000..77a0ac64b --- /dev/null +++ b/test_regress/t/t_covergroup_ref_default_unsup.py @@ -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('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_ref_default_unsup.v b/test_regress/t/t_covergroup_ref_default_unsup.v new file mode 100644 index 000000000..8cada21d7 --- /dev/null +++ b/test_regress/t/t_covergroup_ref_default_unsup.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + bit value; + + covergroup cg_ref(ref bit arg = value); + cp: coverpoint arg; + endgroup + + covergroup cg_const_ref(const ref bit arg = value); + cp: coverpoint arg; + endgroup + + cg_ref ref_cg = new; + cg_const_ref const_ref_cg = new; +endmodule diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index dbe69ab5f..0ddc89233 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -1288,6 +1288,57 @@ package Vt_debug_emitv___024unit; endfunction endcovergroup Vt_debug_emitv___vlAnonCG_cg_in_class cg_in_classVt_debug_emitv___vlAnonCG_cg_in_class; + covergroup Vt_debug_emitv___vlAnonCG_cg_ref; + int signed value; + function new; + const ref int signed value; + value = value; + cp_ref: coverpoint value; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + Vt_debug_emitv___vlAnonCG_cg_ref cg_refVt_debug_emitv___vlAnonCG_cg_ref; function new; cg_in_class = new(); endfunction diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 1782dd663..caeac8094 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -30,6 +30,9 @@ class Cls; bins two = {2}; } endgroup + covergroup cg_ref(ref int value); + cp_ref: coverpoint value; + endgroup function new; cg_in_class = new; endfunction diff --git a/test_regress/t/t_interface_virtual.v b/test_regress/t/t_interface_virtual.v index fca1da253..a517f86d8 100644 --- a/test_regress/t/t_interface_virtual.v +++ b/test_regress/t/t_interface_virtual.v @@ -27,12 +27,29 @@ endclass module t; - PBus ia (), ib (); + PBus ia (), ib (), ic[2] (); virtual PBus va, vb; virtual PBus.phy pa, pb; + virtual PBus.phy pa_array[2], pb_array[2]; Cls ca, cb; Clsgen #(virtual PBus) gen; + function automatic int interface_args( + input virtual PBus.phy input_vif, ref virtual PBus.phy ref_vif, + const ref virtual PBus.phy const_ref_vif, input virtual PBus.phy input_vifs[2], + ref virtual PBus.phy ref_vifs[2], const ref virtual PBus.phy const_ref_vifs[2]); + int result = 0; + if (input_vif.addr == 8'ha0) ++result; + if (ref_vif.addr == 8'hb0) ++result; + if (const_ref_vif.addr == 8'ha0) ++result; + if (input_vifs[0].addr == 8'h01) ++result; + if (ref_vifs[0].addr == 8'ha0) ++result; + if (const_ref_vifs[0].addr == 8'hb0) ++result; + ref_vif = input_vifs[0]; + ref_vifs[0] = input_vifs[1]; + return result; + endfunction + initial begin if (va != null) $stop; if (null != va) $stop; @@ -101,6 +118,16 @@ module t; $display("gen.x[0].addr=%x", gen.x[0].addr, " gen.x[1].addr=%x", gen.x[1].addr); $display("gen=%p", gen); + ic[0].addr = 8'h01; + ic[1].addr = 8'h02; + pa_array[0] = ia; + pa_array[1] = ib; + pb_array[0] = ib; + pb_array[1] = ia; + if (interface_args(ia, pb, pa, ic, pa_array, pb_array) != 6) $stop; + if (pb.addr != 8'h01) $stop; + if (pa_array[0].addr != 8'h02) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_interface_virtual_bad.out b/test_regress/t/t_interface_virtual_bad.out index e1bbe8819..ddfc3670e 100644 --- a/test_regress/t/t_interface_virtual_bad.out +++ b/test_regress/t/t_interface_virtual_bad.out @@ -32,4 +32,48 @@ : ... Suggested alternative: 'grant' 44 | $display("q8.grant=", p8.grant, " v8.grant=", v8.grant, v8_phy.addr, v8.gran); | ^~~~ +%Error: t/t_interface_virtual_bad.v:74:14: Virtual interface argument 'vif' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 74 | take_vif(q8); + | ^~ +%Error: t/t_interface_virtual_bad.v:75:18: Ref virtual interface argument 'vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 75 | take_ref_vif(v8); + | ^~ +%Error: t/t_interface_virtual_bad.v:76:18: Ref virtual interface argument 'vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 76 | take_ref_vif(p8); + | ^~ +%Error: t/t_interface_virtual_bad.v:77:24: Ref virtual interface argument 'vif' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 77 | take_const_ref_vif(v8); + | ^~ +%Error: t/t_interface_virtual_bad.v:78:20: Virtual interface argument 'vifs' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 78 | take_vif_array(q8_array); + | ^~~~~~~~ +%Error: t/t_interface_virtual_bad.v:79:24: Ref virtual interface argument 'vifs' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 79 | take_ref_vif_array(v8_array); + | ^~~~~~~~ +%Error: t/t_interface_virtual_bad.v:80:24: Ref virtual interface argument 'vifs' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 80 | take_ref_vif_array(p8_array); + | ^~~~~~~~ +%Error: t/t_interface_virtual_bad.v:81:30: Ref virtual interface argument 'vifs' requires the same interface type, parameters, and modport. + : ... note: In instance 't' + 81 | take_const_ref_vif_array(v8_array); + | ^~~~~~~~ +%Error: t/t_interface_virtual_bad.v:82:14: Virtual interface argument 'vif' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 82 | take_vif(v8_array); + | ^~~~~~~~ +%Error: t/t_interface_virtual_bad.v:83:20: Virtual interface argument 'vifs' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 83 | take_vif_array(v8); + | ^~ +%Error: t/t_interface_virtual_bad.v:84:20: Virtual interface argument 'vifs' requires a compatible interface type, parameters, and modport. + : ... note: In instance 't' + 84 | take_vif_array(v8_array3); + | ^~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_interface_virtual_bad.v b/test_regress/t/t_interface_virtual_bad.v index 8c484e9c1..1f0239a4b 100644 --- a/test_regress/t/t_interface_virtual_bad.v +++ b/test_regress/t/t_interface_virtual_bad.v @@ -46,4 +46,41 @@ module t; $write("*-* All Finished *-*\n"); $finish; end + + PBus p8_array[2] (); + QBus q8_array[2] (); + virtual PBus v8_array[2]; + virtual PBus v8_array3[3]; + + function automatic void take_vif(input virtual PBus.phy vif); + endfunction + + function automatic void take_ref_vif(ref virtual PBus.phy vif); + endfunction + + function automatic void take_const_ref_vif(const ref virtual PBus.phy vif); + endfunction + + function automatic void take_vif_array(input virtual PBus.phy vifs[2]); + endfunction + + function automatic void take_ref_vif_array(ref virtual PBus.phy vifs[2]); + endfunction + + function automatic void take_const_ref_vif_array(const ref virtual PBus.phy vifs[2]); + endfunction + + initial begin + take_vif(q8); + take_ref_vif(v8); + take_ref_vif(p8); + take_const_ref_vif(v8); + take_vif_array(q8_array); + take_ref_vif_array(v8_array); + take_ref_vif_array(p8_array); + take_const_ref_vif_array(v8_array); + take_vif(v8_array); + take_vif_array(v8); + take_vif_array(v8_array3); + end endmodule