From f1389497c1c4d1475dc0f2c0f3b7e218d96d0e5c Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Fri, 27 Mar 2026 21:27:08 +0000 Subject: [PATCH] Checkpoint on closing coverage holes Signed-off-by: Matthew Ballance --- src/V3Active.cpp | 17 +- src/V3AstNodeOther.h | 33 +- src/V3AstNodes.cpp | 8 - src/V3Covergroup.cpp | 268 ++++----------- src/V3LinkParse.cpp | 6 +- src/V3Timing.cpp | 4 +- src/V3Width.cpp | 21 +- test_regress/t/t_covergroup_array_bins.out | 2 + test_regress/t/t_covergroup_array_bins.py | 2 +- test_regress/t/t_covergroup_array_bins.v | 36 ++- test_regress/t/t_covergroup_auto_bin_max.out | 7 + test_regress/t/t_covergroup_auto_bin_max.v | 33 +- test_regress/t/t_covergroup_auto_bins.out | 11 + test_regress/t/t_covergroup_auto_bins.v | 60 +++- test_regress/t/t_covergroup_cross.out | 16 + test_regress/t/t_covergroup_cross.v | 40 +++ test_regress/t/t_covergroup_default_bins.out | 1 + test_regress/t/t_covergroup_default_bins.v | 19 +- test_regress/t/t_covergroup_iff.out | 5 + test_regress/t/t_covergroup_iff.v | 82 ++++- test_regress/t/t_covergroup_ignore_bins.out | 9 + test_regress/t/t_covergroup_ignore_bins.v | 41 ++- test_regress/t/t_covergroup_illegal_bins.out | 7 + test_regress/t/t_covergroup_illegal_bins.v | 32 +- test_regress/t/t_covergroup_option.v | 41 ++- test_regress/t/t_covergroup_trans.out | 1 + test_regress/t/t_covergroup_trans.py | 2 +- test_regress/t/t_covergroup_trans.v | 9 +- test_regress/t/t_covergroup_unsup.out | 305 ++++++++++-------- test_regress/t/t_covergroup_unsup.v | 25 ++ test_regress/t/t_covergroup_wildcard_bins.out | 4 +- test_regress/t/t_covergroup_wildcard_bins.v | 14 + .../t/t_vlcov_covergroup.annotate.out | 40 +++ test_regress/t/t_vlcov_covergroup.out | 50 ++- 34 files changed, 796 insertions(+), 455 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 7b031da41..386fa931c 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -665,10 +665,11 @@ class CovergroupCollectVisitor final : public VNVisitor { } void visit(AstCovergroup* nodep) override { - if (!m_classp) return; + // V3Covergroup guarantees: only supported-event covergroups survive to V3Active, + // and they are always inside a covergroup class (so m_classp is set). // Unlink eventp from cgp so it survives cgp's deletion, // then take ownership in the map for use during the second pass. - if (nodep->eventp()) m_state.m_samplingEvents[m_classp] = nodep->eventp()->unlinkFrBack(); + m_state.m_samplingEvents[m_classp] = nodep->eventp()->unlinkFrBack(); nodep->unlinkFrBack(); VL_DO_DANGLING(nodep->deleteTree(), nodep); } @@ -701,11 +702,11 @@ class CovergroupInjectVisitor final : public VNVisitor { void visit(AstVarScope* nodep) override { // Get the underlying var AstVar* const varp = nodep->varp(); - if (!varp) return; + if (!varp) return; // LCOV_EXCL_BR_LINE -- AstVarScope always has non-null varp // Check if the variable is of covergroup class type const AstNodeDType* const dtypep = varp->dtypep(); - if (!dtypep) return; + if (!dtypep) return; // LCOV_EXCL_BR_LINE -- typed vars always have non-null dtypep const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType); if (!classRefp) return; @@ -719,12 +720,10 @@ class CovergroupInjectVisitor final : public VNVisitor { return; // No automatic sampling for this covergroup AstSenTree* const eventp = evtIt->second; - // Get the sample CFunc from the map populated during the first pass + // V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc const auto it = m_state.m_sampleFuncs.find(classp); - if (it == m_state.m_sampleFuncs.end()) { - UINFO(4, "Could not find sample() CFunc for covergroup " << classp->name()); - return; - } + UASSERT_OBJ(it != m_state.m_sampleFuncs.end(), nodep, + "No sample() CFunc found for covergroup " << classp->name()); AstCFunc* const sampleCFuncp = it->second; // Create a VarRef to the covergroup instance for the method call diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 9f858955f..de9d72500 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1090,22 +1090,6 @@ public: bool isArray() const { return m_isArray; } void isArray(bool flag) { m_isArray = flag; } }; -class AstCoverCrossBins final : public AstNode { - // Cross-point bin definition - // @astgen op1 := selectp : Optional[AstCoverSelectExpr] - const string m_name; // Bin name - -public: - AstCoverCrossBins(FileLine* fl, const string& name, AstCoverSelectExpr* selectp) - : ASTGEN_SUPER_CoverCrossBins(fl) - , m_name{name} { - this->selectp(selectp); - } - ASTGEN_MEMBERS_AstCoverCrossBins; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - string name() const override VL_MT_STABLE { return m_name; } -}; class AstCoverOption final : public AstNode { // Coverage-option assignment // @astgen op1 := valuep : AstNodeExpr @@ -1122,18 +1106,6 @@ public: void dumpJson(std::ostream& str) const override; VCoverOptionType optionType() const { return m_type; } }; -class AstCoverSelectExpr final : public AstNode { - // Represents a cross-bins selection expression (eg binsof(cp1) intersect {1,2}) - // @astgen op1 := exprp : AstNodeExpr -public: - AstCoverSelectExpr(FileLine* fl, AstNodeExpr* exprp) - : ASTGEN_SUPER_CoverSelectExpr(fl) { - this->exprp(exprp); - } - ASTGEN_MEMBERS_AstCoverSelectExpr; - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; -}; class AstCoverTransItem final : public AstNode { // Represents a single transition item: value or value[*N] or value[->N] or value[=N] // @astgen op1 := valuesp : List[AstNode] @@ -2722,9 +2694,8 @@ public: }; class AstCoverCross final : public AstNodeFuncCovItem { // @astgen op1 := itemsp : List[AstCoverpointRef] - // @astgen op2 := binsp : List[AstCoverCrossBins] // post-LinkParse only - // @astgen op3 := optionsp : List[AstCoverOption] // post-LinkParse only - // @astgen op4 := rawBodyp : List[AstNode] // Parse: raw cross_body items; + // @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only + // @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items; // // post-LinkParse: empty public: AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 17313e02d..c613e3760 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -3533,10 +3533,6 @@ void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::du void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); } -void AstCoverCrossBins::dump(std::ostream& str) const { this->AstNode::dump(str); } - -void AstCoverCrossBins::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); } - void AstCoverOption::dump(std::ostream& str) const { this->AstNode::dump(str); str << " " << m_type.ascii(); @@ -3550,7 +3546,3 @@ void AstCoverOption::dumpJson(std::ostream& str) const { void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstCoverpointRef::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); } - -void AstCoverSelectExpr::dump(std::ostream& str) const { this->AstNode::dump(str); } - -void AstCoverSelectExpr::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); } diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 5f877b7c5..0132c47d9 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -63,10 +63,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Track coverpoints that need previous value tracking (for transition bins) std::map m_prevValueVars; // coverpoint -> prev_value variable - // Track sequence state variables for multi-value transition bins - // Key is bin pointer, value is state position variable - std::map m_seqStateVars; // transition bin -> sequence state variable - VMemberMap m_memberMap; // Member names cached for fast lookup // METHODS @@ -79,8 +75,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { } void processCovergroup() { - if (!m_covergroupp) return; - UINFO(4, "Processing covergroup: " << m_covergroupp->name() << " with " << m_coverpoints.size() << " coverpoints and " << m_coverCrosses.size() << " crosses"); @@ -115,19 +109,14 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Find and expand any automatic bins AstNode* prevBinp = nullptr; for (AstNode* binp = coverpointp->binsp(); binp;) { - AstCoverBin* const cbinp = VN_CAST(binp, CoverBin); + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); AstNode* const nextBinp = binp->nextp(); - if (cbinp && cbinp->binsType() == VCoverBinsType::BINS_AUTO) { + if (cbinp->binsType() == VCoverBinsType::BINS_AUTO) { UINFO(4, " Expanding automatic bin: " << cbinp->name()); // Get array size - must be a constant AstNodeExpr* const sizep = cbinp->arraySizep(); - if (!sizep) { - cbinp->v3error("Automatic bins requires array size [N]"); // LCOV_EXCL_LINE - binp = nextBinp; - continue; - } // Evaluate as constant const AstConst* constp = VN_CAST(sizep, Const); @@ -213,7 +202,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { } // Fall back to covergroup-level auto_bin_max if not set at coverpoint level if (autoBinMaxOut < 0) { - if (m_covergroupp && m_covergroupp->cgAutoBinMax() >= 0) { + if (m_covergroupp->cgAutoBinMax() >= 0) { autoBinMaxOut = m_covergroupp->cgAutoBinMax(); } else { autoBinMaxOut = 64; // Default per IEEE 1800-2017 @@ -221,34 +210,25 @@ class FunctionalCoverageVisitor final : public VNVisitor { } } - // Extract individual values from a range expression + // Extract individual values from a range expression list. + // Iterates over all siblings (nextp) in the list, handling AstConst (single value) + // and AstInsideRange ([lo:hi]). void extractValuesFromRange(AstNode* nodep, std::set& values) { - if (!nodep) return; - - if (AstConst* constp = VN_CAST(nodep, Const)) { - // Single constant value - values.insert(constp->toUQuad()); - } else if (AstInsideRange* rangep = VN_CAST(nodep, InsideRange)) { - // Range [lo:hi] - AstNodeExpr* const lhsp = V3Const::constifyEdit(rangep->lhsp()); - AstNodeExpr* const rhsp = V3Const::constifyEdit(rangep->rhsp()); - AstConst* const loConstp = VN_CAST(lhsp, Const); - AstConst* const hiConstp = VN_CAST(rhsp, Const); - if (loConstp && hiConstp) { - const uint64_t lo = loConstp->toUQuad(); - const uint64_t hi = hiConstp->toUQuad(); - // Add all values in range (but limit to reasonable size) - if (hi - lo < 1000) { // Sanity check - for (uint64_t v = lo; v <= hi && v <= lo + 1000; v++) values.insert(v); + for (AstNode* np = nodep; np; np = np->nextp()) { + if (AstConst* constp = VN_CAST(np, Const)) { + values.insert(constp->toUQuad()); + } else if (AstInsideRange* rangep = VN_CAST(np, InsideRange)) { + AstNodeExpr* const lhsp = V3Const::constifyEdit(rangep->lhsp()); + AstNodeExpr* const rhsp = V3Const::constifyEdit(rangep->rhsp()); + AstConst* const loConstp = VN_CAST(lhsp, Const); + AstConst* const hiConstp = VN_CAST(rhsp, Const); + if (loConstp && hiConstp) { + const uint64_t lo = loConstp->toUQuad(); + const uint64_t hi = hiConstp->toUQuad(); + for (uint64_t v = lo; v <= hi; v++) values.insert(v); } } } - - // Recurse into list of nodes - extractValuesFromRange(nodep->op1p(), values); - extractValuesFromRange(nodep->op2p(), values); - extractValuesFromRange(nodep->op3p(), values); - extractValuesFromRange(nodep->op4p(), values); } // Single-pass categorization: determine whether any regular (non-ignore/illegal) bins exist @@ -257,8 +237,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { std::set& excludedOut) { hasRegularOut = false; for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { - AstCoverBin* const cbinp = VN_CAST(binp, CoverBin); - if (!cbinp) continue; + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); const VCoverBinsType btype = cbinp->binsType(); if (btype == VCoverBinsType::BINS_IGNORE || btype == VCoverBinsType::BINS_ILLEGAL) { if (AstNode* rangep = cbinp->rangesp()) { @@ -342,22 +321,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { const uint64_t lo = i * binSize; const uint64_t hi = (i == numBins - 1) ? maxVal : ((i + 1) * binSize - 1); - // Check if entire range is excluded - bool anyValid = false; - for (uint64_t v = lo; v <= hi && v <= lo + 1000; v++) { - if (excluded.find(v) == excluded.end()) { - anyValid = true; - break; - } - } - - if (!anyValid && (hi - lo < 1000)) { - // Skip this bin entirely if all values are excluded - UINFO(4, " Skipping bin [" << lo << ":" << hi << "] - all values excluded" - ); - continue; - } - // Create constants for range AstConst* const loConstp = new AstConst{ coverpointp->fileline(), V3Number(coverpointp->fileline(), width, lo)}; @@ -412,10 +375,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Create state position variable for multi-value transition bins // Tracks position in sequence: 0=not started, 1=seen first item, etc. AstVar* createSequenceStateVar(AstCoverpoint* coverpointp, AstCoverBin* binp) { - // Check if already created - const auto it = m_seqStateVars.find(binp); - if (it != m_seqStateVars.end()) return it->second; - // Create variable to track sequence position const string varName = "__Vseqpos_" + coverpointp->name() + "_" + binp->name(); // Use 8-bit integer for state position (sequences rarely > 255 items) @@ -432,25 +391,14 @@ class FunctionalCoverageVisitor final : public VNVisitor { new AstConst{stateVarp->fileline(), AstConst::WidthedValue{}, 8, 0}}; m_constructorp->addStmtsp(initStmtp); - m_seqStateVars[binp] = stateVarp; return stateVarp; } void generateCoverpointCode(AstCoverpoint* coverpointp) { - if (!m_sampleFuncp || !m_constructorp) { - coverpointp->v3warn(E_UNSUPPORTED, - "Coverpoint without sample() or constructor"); // LCOV_EXCL_LINE - return; - } - UINFO(4, " Generating code for coverpoint: " << coverpointp->name()); // Get the coverpoint expression AstNodeExpr* const exprp = coverpointp->exprp(); - if (!exprp) { - coverpointp->v3warn(E_UNSUPPORTED, "Coverpoint without expression"); // LCOV_EXCL_LINE - return; - } // Expand automatic bins before processing expandAutomaticBins(coverpointp, exprp); @@ -470,8 +418,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { std::vector defaultBins; bool hasTransition = false; for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { - AstCoverBin* const cbinp = VN_CAST(binp, CoverBin); - if (!cbinp) continue; + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); // Defer default bins to second pass if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT) { @@ -481,7 +428,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Handle array bins: create separate bin for each value/transition if (cbinp->isArray()) { - if (cbinp->binsType() == VCoverBinsType::BINS_TRANSITION) { + if (cbinp->transp()) { // transition bin (includes illegal_bins with transitions) hasTransition = true; generateTransitionArrayBins(coverpointp, cbinp, exprp, atLeastValue); } else { @@ -512,8 +459,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Classes use "v_covergroup/" hier prefix vs modules // Generate bin matching code in sample() - // Handle transition bins specially - if (cbinp->binsType() == VCoverBinsType::BINS_TRANSITION) { + // Handle transition bins specially (includes illegal_bins with transition syntax) + if (cbinp->transp()) { hasTransition = true; generateTransitionBinMatchCode(coverpointp, cbinp, exprp, varp); } else { @@ -564,7 +511,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstNodeExpr* fullCondp = buildBinCondition(binp, exprp); if (!fullCondp) { - UINFO(4, " No valid conditions generated"); + // Reachable: e.g. 'ignore_bins ib = default' creates a BINS_IGNORE bin + // with null rangesp. Skipping match code generation is correct in that case. return; } @@ -588,8 +536,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstIf* const ifp = new AstIf{binp->fileline(), fullCondp, stmtp, nullptr}; UINFO(4, " Adding bin match if statement to sample function"); - if (!m_sampleFuncp) - binp->v3fatalSrc("m_sampleFuncp is null when trying to add bin match code"); + if (!m_sampleFuncp) // LCOV_EXCL_BR_LINE + binp->v3fatalSrc("m_sampleFuncp is null when trying to add bin match code"); // LCOV_EXCL_LINE m_sampleFuncp->addStmtsp(ifp); UINFO(4, " Successfully added if statement for bin: " << binp->name()); } @@ -604,8 +552,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstNodeExpr* anyBinMatchp = nullptr; for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { - AstCoverBin* const cbinp = VN_CAST(binp, CoverBin); - if (!cbinp) continue; + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); // Skip default, ignore, and illegal bins if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT @@ -660,10 +607,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Get the (single) transition set AstCoverTransSet* const transSetp = binp->transp(); - if (!transSetp) { - binp->v3error("Transition bin without transition set"); // LCOV_EXCL_LINE - return; - } // Use the helper function to generate code for this transition generateSingleTransitionCode(coverpointp, binp, exprp, hitVarp, transSetp); @@ -693,8 +636,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { for (size_t state = 0; state < items.size(); ++state) { AstCaseItem* caseItemp = generateTransitionStateCase(coverpointp, binp, exprp, hitVarp, stateVarp, items, state); - - if (caseItemp) casep->addItemsp(caseItemp); + casep->addItemsp(caseItemp); } // Add default case (reset to state 0) to prevent CASEINCOMPLETE warnings, @@ -721,11 +663,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Build condition for current value matching expected item at this state AstNodeExpr* matchCondp = buildTransitionItemCondition(items[state], exprp); - if (!matchCondp) { - binp->v3error("Could not build transition condition for state " // LCOV_EXCL_LINE - + std::to_string(state)); // LCOV_EXCL_LINE - return nullptr; - } // Apply iff condition if present if (AstNodeExpr* iffp = coverpointp->iffp()) { @@ -863,40 +800,23 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstNodeExpr* buildTransitionItemCondition(AstCoverTransItem* itemp, AstNodeExpr* exprp) { AstNodeExpr* condp = nullptr; - // Get values from the transition item for (AstNode* valp = itemp->valuesp(); valp; valp = valp->nextp()) { AstNodeExpr* singleCondp = nullptr; if (AstConst* constp = VN_CAST(valp, Const)) { - // Simple value: check equality singleCondp = new AstEq{constp->fileline(), exprp->cloneTree(false), constp->cloneTree(false)}; - } else if (AstRange* rangep = VN_CAST(valp, Range)) { - singleCondp = makeRangeCondition(rangep->fileline(), exprp, rangep->leftp(), - rangep->rightp()); - } else if (AstInsideRange* inrangep = VN_CAST(valp, InsideRange)) { - singleCondp = makeRangeCondition(inrangep->fileline(), exprp, inrangep->lhsp(), - inrangep->rhsp()); } else { - // Unknown node type - try to handle as expression - UINFO(4, " Transition item has unknown value node type: " << valp->typeName() - ); - // For now, just skip unknown types - this prevents crashes - continue; + valp->v3fatalSrc("Unexpected node type in transition item: " << valp->typeName()); } - // OR together multiple values - if (singleCondp) { - if (condp) { - condp = new AstOr{itemp->fileline(), condp, singleCondp}; - } else { - condp = singleCondp; - } + if (condp) { + condp = new AstOr{itemp->fileline(), condp, singleCondp}; + } else { + condp = singleCondp; } } - if (!condp) UINFO(4, " No valid transition conditions could be built"); - return condp; } @@ -909,22 +829,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Extract all values from the range list std::vector values; for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) { - if (AstRange* const rangenodep = VN_CAST(rangep, Range)) { - // For ranges [min:max], create bins for each value - AstConst* const minConstp = VN_CAST(rangenodep->leftp(), Const); - AstConst* const maxConstp = VN_CAST(rangenodep->rightp(), Const); - if (minConstp && maxConstp) { - const int minVal = minConstp->toSInt(); - const int maxVal = maxConstp->toSInt(); - for (int val = minVal; val <= maxVal; ++val) { - values.push_back( - new AstConst{rangenodep->fileline(), AstConst::Signed32{}, val}); - } - } else { - arrayBinp->v3error("covergroup value range"); - return; - } - } else if (AstInsideRange* const insideRangep = VN_CAST(rangep, InsideRange)) { + if (AstInsideRange* const insideRangep = VN_CAST(rangep, InsideRange)) { // For InsideRange [min:max], create bins for each value AstNodeExpr* const minp = V3Const::constifyEdit(insideRangep->lhsp()); AstNodeExpr* const maxp = V3Const::constifyEdit(insideRangep->rhsp()); @@ -937,10 +842,12 @@ class FunctionalCoverageVisitor final : public VNVisitor { ); for (int val = minVal; val <= maxVal; ++val) { values.push_back( - new AstConst{insideRangep->fileline(), AstConst::Signed32{}, val}); + new AstConst{insideRangep->fileline(), AstConst::WidthedValue{}, + (int)exprp->width(), (uint32_t)val}); } } else { - arrayBinp->v3error("covergroup value range"); + arrayBinp->v3error("Non-constant expression in array bins range; " + "range bounds must be constants"); return; } } else { @@ -1018,14 +925,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { } } - if (transSets.empty()) { - arrayBinp->v3error("Transition array bin without transition sets"); // LCOV_EXCL_LINE - return; - } - UINFO(4, " Found " << transSets.size() << " transition sets"); - - // Create a separate bin for each transition sequence int index = 0; for (AstCoverTransSet* transSetp : transSets) { // Create bin name: originalName[index] @@ -1062,9 +962,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Get or create previous value variable AstVar* const prevVarp = createPrevValueVar(coverpointp, exprp); - if (!transSetp) { + if (!transSetp) { // LCOV_EXCL_BR_LINE binp->v3error("Transition bin without transition set"); // LCOV_EXCL_LINE - return; + return; // LCOV_EXCL_LINE } // Get transition items (the sequence: item1 => item2 => item3) @@ -1102,11 +1002,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstNodeExpr* const cond1p = buildTransitionItemCondition(items[0], prevVarp); AstNodeExpr* const cond2p = buildTransitionItemCondition(items[1], exprp); - if (!cond1p || !cond2p) { - binp->v3error("Could not build transition conditions"); // LCOV_EXCL_LINE - return; - } - // Combine: prev matches val1 AND current matches val2 AstNodeExpr* fullCondp = new AstAnd{binp->fileline(), cond1p, cond2p}; @@ -1208,10 +1103,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { for (size_t i = 0; i < bins.size(); ++i) { AstNodeExpr* const exprp = coverpointRefs[i]->exprp(); - if (!exprp) continue; - AstNodeExpr* const condp = buildBinCondition(bins[i], exprp); - if (!condp) continue; if (fullCondp) { fullCondp = new AstAnd{crossp->fileline(), fullCondp, condp}; @@ -1220,8 +1112,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { } } - if (!fullCondp) return; - // Generate: if (cond1 && cond2 && ... && condN) { ++varName; } AstNodeStmt* const incrp = makeBinHitIncrement(crossp->fileline(), hitVarp); @@ -1230,12 +1120,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { } void generateCrossCode(AstCoverCross* crossp) { - if (!m_sampleFuncp || !m_constructorp) { - crossp->v3warn(E_UNSUPPORTED, - "Cross coverage without sample() or constructor"); // LCOV_EXCL_LINE - return; - } - UINFO(4, " Generating code for cross: " << crossp->name()); // Resolve coverpoint references and build list @@ -1266,12 +1150,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { itemp = nextp; } - if (coverpointRefs.size() < 2) { - crossp->v3warn(E_UNSUPPORTED, - "Cross coverage requires at least 2 coverpoints"); // LCOV_EXCL_LINE - return; - } - UINFO(4, " Generating " << coverpointRefs.size() << "-way cross"); // Collect bins from all coverpoints (excluding ignore/illegal bins) @@ -1279,8 +1157,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { for (AstCoverpoint* cpp : coverpointRefs) { std::vector cpBins; for (AstNode* binp = cpp->binsp(); binp; binp = binp->nextp()) { - AstCoverBin* const cbinp = VN_CAST(binp, CoverBin); - if (cbinp && cbinp->binsType() == VCoverBinsType::BINS_USER) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + if (cbinp->binsType() == VCoverBinsType::BINS_USER) { cpBins.push_back(cbinp); } } @@ -1383,11 +1261,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstFunc* const getInstCoveragep = VN_CAST(m_memberMap.findMember(m_covergroupp, "get_inst_coverage"), Func); - if (!getCoveragep || !getInstCoveragep) { - UINFO(4, " Warning: Could not find get_coverage methods"); - return; - } - // Even if there are no bins, we still need to generate the coverage methods // Empty covergroups should return 100% coverage if (m_binInfos.empty()) { @@ -1397,24 +1270,20 @@ class FunctionalCoverageVisitor final : public VNVisitor { } // Generate code for get_inst_coverage() - if (getInstCoveragep) generateCoverageMethodBody(getInstCoveragep); + generateCoverageMethodBody(getInstCoveragep); // Generate code for get_coverage() (type-level) // NOTE: Full type-level coverage requires instance tracking infrastructure // For now, return 0.0 as a placeholder - if (getCoveragep) { - AstVar* const returnVarp = VN_AS(getCoveragep->fvarp(), Var); - if (returnVarp) { - // TODO: Implement proper type-level coverage aggregation - // This requires tracking all instances and averaging their coverage - // For now, return 0.0 - getCoveragep->addStmtsp(new AstAssign{ - getCoveragep->fileline(), - new AstVarRef{getCoveragep->fileline(), returnVarp, VAccess::WRITE}, - new AstConst{getCoveragep->fileline(), AstConst::RealDouble{}, 0.0}}); - UINFO(4, " Added placeholder get_coverage() (returns 0.0)"); - } - } + AstVar* const coverageReturnVarp = VN_AS(getCoveragep->fvarp(), Var); + // TODO: Implement proper type-level coverage aggregation + // This requires tracking all instances and averaging their coverage + // For now, return 0.0 + getCoveragep->addStmtsp(new AstAssign{ + getCoveragep->fileline(), + new AstVarRef{getCoveragep->fileline(), coverageReturnVarp, VAccess::WRITE}, + new AstConst{getCoveragep->fileline(), AstConst::RealDouble{}, 0.0}}); + UINFO(4, " Added placeholder get_coverage() (returns 0.0)"); } void generateCoverageMethodBody(AstFunc* funcp) { @@ -1437,11 +1306,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Any parser-generated initialization of returnVar is overridden by our assignment. UINFO(4, " Empty covergroup, returning 100.0"); AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var); - if (returnVarp) { - funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE}, - new AstConst{fl, AstConst::RealDouble{}, 100.0}}); - UINFO(4, " Added assignment to return 100.0"); - } + funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::RealDouble{}, 100.0}}); + UINFO(4, " Added assignment to return 100.0"); return; } @@ -1478,10 +1345,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Find the return variable AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var); - if (!returnVarp) { - UINFO(4, " Warning: No return variable found in " << funcp->name()); - return; - } // Calculate coverage: (covered_count / total_bins) * 100.0 // return_var = (double)covered_count / (double)total_bins * 100.0 @@ -1519,15 +1382,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { if (m_binInfos.empty()) return; - // We need to add the registration code to the constructor - // The registration should happen after member variables are initialized - if (!m_constructorp) { - m_covergroupp->v3warn( - E_UNSUPPORTED, - "Cannot generate coverage registration without constructor"); // LCOV_EXCL_LINE - return; - } - // For each bin, generate a VL_COVER_INSERT call // The calls use CCall nodes to invoke VL_COVER_INSERT macro for (const BinInfo& binInfo : m_binInfos) { @@ -1630,16 +1484,18 @@ class FunctionalCoverageVisitor final : public VNVisitor { AstNode* const nextp = itemp->nextp(); if (AstCovergroup* const cgp = VN_CAST(itemp, Covergroup)) { // Store the event in the global map for V3Active to retrieve later - if (cgp->eventp()) { + // V3LinkParse only creates this sentinel AstCovergroup node when a clocking + // event exists, so cgp->eventp() is always non-null here. + if (cgp->eventp()) { // LCOV_EXCL_BR_LINE // Check if the clocking event references a member variable (unsupported) // Clocking events should be on signals/nets, not class members bool eventUnsupported = false; for (AstNode* senp = cgp->eventp()->sensesp(); senp; senp = senp->nextp()) { - if (AstSenItem* const senItemp = VN_CAST(senp, SenItem)) { - if (AstVarRef* const varrefp + if (AstSenItem* const senItemp = VN_CAST(senp, SenItem)) { // LCOV_EXCL_BR_LINE + if (AstVarRef* const varrefp // LCOV_EXCL_BR_LINE = VN_CAST(senItemp->sensp(), VarRef)) { - if (varrefp->varp() && varrefp->varp()->isClassMember()) { + if (varrefp->varp()->isClassMember()) { cgp->v3warn(COVERIGN, "Unsupported: 'covergroup' clocking event " "on member variable"); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ee99772bf..5e0bcd57f 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -1363,16 +1363,14 @@ class LinkParseVisitor final : public VNVisitor { void visit(AstCoverCross* nodep) override { cleanFileline(nodep); - // Distribute the parse-time raw cross_body list (rawBodyp, op4) into the - // typed binsp and optionsp slots. Nodes are properly in-tree here so + // Distribute the parse-time raw cross_body list (rawBodyp, op3) into the + // typed optionsp slot. Nodes are properly in-tree here so // unlinkFrBack() works cleanly with no bison-list hackery. for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) { nextp = itemp->nextp(); itemp->unlinkFrBack(); if (AstCoverOption* const optp = VN_CAST(itemp, CoverOption)) { nodep->addOptionsp(optp); - } else if (AstCoverCrossBins* const binp = VN_CAST(itemp, CoverCrossBins)) { - nodep->addBinsp(binp); } else { // AstCgOptionAssign, AstFunc, and other unsupported items VL_DO_DANGLING(itemp->deleteTree(), itemp); diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 5f57762f7..e8e99a512 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -344,14 +344,14 @@ class TimingSuspendableVisitor final : public VNVisitor { } void visit(AstNodeCCall* nodep) override { AstCFunc* funcp = nodep->funcp(); - if (!funcp) { + if (!funcp) { // LCOV_EXCL_BR_LINE -- AstNodeCCall always has non-null funcp iterateChildren(nodep); return; } // Skip if we're not inside a function/procedure (m_procp would be null) // This can happen for calls in Active nodes at module scope - if (!m_procp) { + if (!m_procp) { // LCOV_EXCL_BR_LINE -- m_procp is always set when CCall is inside a function iterateChildren(nodep); return; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4f009d90a..6b22ec895 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1726,19 +1726,18 @@ class WidthVisitor final : public VNVisitor { if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); } void visit(AstCgOptionAssign* nodep) override { - // Extract covergroup option values and store in AstClass before deleting - if (m_cgClassp) { - // Process supported options - if (nodep->name() == "auto_bin_max" && !nodep->typeOption()) { - // Extract constant value - if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) { - m_cgClassp->cgAutoBinMax(constp->toSInt()); - UINFO(6, " Covergroup " << m_cgClassp->name() << " option.auto_bin_max = " - << constp->toSInt() << endl); - } + // Extract covergroup option values and store in AstClass before deleting. + // m_cgClassp is always set here: AstCgOptionAssign only appears in covergroup + // class bodies, and visitClass sets m_cgClassp before iterating children. + if (nodep->name() == "auto_bin_max" && !nodep->typeOption()) { + // Extract constant value + if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) { + m_cgClassp->cgAutoBinMax(constp->toSInt()); + UINFO(6, " Covergroup " << m_cgClassp->name() << " option.auto_bin_max = " + << constp->toSInt() << endl); } - // Add more options here as needed (weight, goal, at_least, per_instance, comment) } + // Add more options here as needed (weight, goal, at_least, per_instance, comment) // Delete the assignment node (we've extracted the value) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); diff --git a/test_regress/t/t_covergroup_array_bins.out b/test_regress/t/t_covergroup_array_bins.out index 98f5d8f06..4c0767563 100644 --- a/test_regress/t/t_covergroup_array_bins.out +++ b/test_regress/t/t_covergroup_array_bins.out @@ -1,2 +1,4 @@ cg.data.grouped: 2 cg.data.values: 3 +cg2.cp.range_arr: 3 +cg3.cp.range_sized: 3 diff --git a/test_regress/t/t_covergroup_array_bins.py b/test_regress/t/t_covergroup_array_bins.py index 10b6f7cd5..73e47da7a 100755 --- a/test_regress/t/t_covergroup_array_bins.py +++ b/test_regress/t/t_covergroup_array_bins.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile(verilator_flags2=['--coverage']) +test.compile(verilator_flags2=['--coverage --Wno-COVERIGN']) test.execute() diff --git a/test_regress/t/t_covergroup_array_bins.v b/test_regress/t/t_covergroup_array_bins.v index 06283a665..b6e6d44f1 100644 --- a/test_regress/t/t_covergroup_array_bins.v +++ b/test_regress/t/t_covergroup_array_bins.v @@ -4,7 +4,7 @@ // SPDX-FileCopyrightText: 2026 Matthew Ballance // SPDX-License-Identifier: CC0-1.0 -// Test array bins - separate bin per value +// Test array bins - separate bin per value, including InsideRange and AstRange module t; bit [7:0] data; @@ -19,10 +19,28 @@ module t; } endgroup - initial begin - cg cg_inst; + // cg2: exercises InsideRange in array bins (e.g., bins r[] = {[0:3]}) + covergroup cg2; + cp: coverpoint data { + bins range_arr[] = {[0:3]}; // InsideRange -> 4 separate bins + } + endgroup - cg_inst = new(); + // cg3: exercises AstRange in array bins (e.g., bins r[N] = {[lo:hi]}) + covergroup cg3; + cp: coverpoint data { + bins range_sized[4] = {[4:7]}; // AstRange with explicit count + } + endgroup + + initial begin + cg cg_inst; + cg2 cg2_inst; + cg3 cg3_inst; + + cg_inst = new(); + cg2_inst = new(); + cg3_inst = new(); // Hit first array bin value (1) data = 1; @@ -44,6 +62,16 @@ module t; data = 2; cg_inst.sample(); + // Hit range_arr bins (InsideRange [0:3]) + data = 0; cg2_inst.sample(); + data = 1; cg2_inst.sample(); + data = 2; cg2_inst.sample(); + + // Hit range_sized bins (AstRange [4:7]) + data = 4; cg3_inst.sample(); + data = 5; cg3_inst.sample(); + data = 6; cg3_inst.sample(); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_auto_bin_max.out b/test_regress/t/t_covergroup_auto_bin_max.out index 71eb3a496..4f4308cc9 100644 --- a/test_regress/t/t_covergroup_auto_bin_max.out +++ b/test_regress/t/t_covergroup_auto_bin_max.out @@ -10,3 +10,10 @@ cg2.cp_data3.auto_0: 1 cg2.cp_data3.auto_1: 0 cg2.cp_data3.auto_2: 1 cg2.cp_data3.auto_3: 0 +cg3.cp_data3.auto_0: 1 +cg3.cp_data3.auto_1: 1 +cg4.cp.auto_0: 0 +cg4.cp.auto_1: 1 +cg4.cp.auto_2: 1 +cg4.cp.auto_3: 1 +cg4.cp.ign [ignore]: 0 diff --git a/test_regress/t/t_covergroup_auto_bin_max.v b/test_regress/t/t_covergroup_auto_bin_max.v index d2b1c453a..e5ff5bf4b 100644 --- a/test_regress/t/t_covergroup_auto_bin_max.v +++ b/test_regress/t/t_covergroup_auto_bin_max.v @@ -10,24 +10,47 @@ module t; logic [2:0] data3; + logic [3:0] data4; // 4-bit signal for range-bin path tests // Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal covergroup cg1; cp_data3: coverpoint data3; endgroup - // Test 2: auto_bin_max = 4 - creates 4 bins: [0:1],[2:3],[4:5],[6:7] + // Test 2: auto_bin_max = 4 at covergroup level - creates 4 bins: [0:1],[2:3],[4:5],[6:7] covergroup cg2; option.auto_bin_max = 4; cp_data3: coverpoint data3; endgroup + // Test 3: auto_bin_max and at_least at *coverpoint* level (lines 207, 209) + covergroup cg3; + cp_data3: coverpoint data3 { + option.auto_bin_max = 2; // coverpoint-level: creates 2 bins [0:3],[4:7] + option.at_least = 3; // coverpoint-level at_least + } + endgroup + + // Test 4: range-bin skip path (lines 287, 356-359). + // auto_bin_max=4 on 4-bit signal → 4 range bins: [0:3],[4:7],[8:11],[12:15]. + // ignore_bins {[0:3]} excludes all values in the first range → that bin is skipped. + covergroup cg4; + option.auto_bin_max = 4; + cp: coverpoint data4 { + ignore_bins ign = {[0:3]}; // first range excluded from coverage + } + endgroup + initial begin cg1 cg1_inst; cg2 cg2_inst; + cg3 cg3_inst; + cg4 cg4_inst; cg1_inst = new; cg2_inst = new; + cg3_inst = new; + cg4_inst = new; data3 = 0; cg1_inst.sample(); data3 = 3; cg1_inst.sample(); @@ -35,6 +58,14 @@ module t; data3 = 0; cg2_inst.sample(); data3 = 4; cg2_inst.sample(); + data3 = 1; cg3_inst.sample(); + data3 = 5; cg3_inst.sample(); + + // Sample valid (non-ignored) values for cg4 + data4 = 4; cg4_inst.sample(); // [4:7] bin + data4 = 8; cg4_inst.sample(); // [8:11] bin + data4 = 12; cg4_inst.sample(); // [12:15] bin + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_auto_bins.out b/test_regress/t/t_covergroup_auto_bins.out index d00dd4ecc..41d820337 100644 --- a/test_regress/t/t_covergroup_auto_bins.out +++ b/test_regress/t/t_covergroup_auto_bins.out @@ -2,3 +2,14 @@ cg.data.auto[0]: 1 cg.data.auto[1]: 1 cg.data.auto[2]: 1 cg.data.auto[3]: 1 +cg2.data64.auto_0: 1 +cg2.data64.auto_1: 1 +cg_4bit.data4.auto[0]: 1 +cg_4bit.data4.auto[1]: 1 +cg_4bit.data4.auto[2]: 1 +cg_4bit.data4.auto[3]: 1 +cg_4bit_excl.data4.auto[0]: 1 +cg_4bit_excl.data4.auto[1]: 0 +cg_4bit_excl.data4.auto[2]: 1 +cg_4bit_excl.data4.auto[3]: 0 +cg_4bit_excl.data4.bad [ignore]: 0 diff --git a/test_regress/t/t_covergroup_auto_bins.v b/test_regress/t/t_covergroup_auto_bins.v index 48dfa303f..62027dd13 100644 --- a/test_regress/t/t_covergroup_auto_bins.v +++ b/test_regress/t/t_covergroup_auto_bins.v @@ -9,35 +9,63 @@ module t; /* verilator lint_off CMPCONST */ - logic [2:0] data; // 3-bit: 0-7 + logic [2:0] data; // 3-bit: 0-7 + logic [3:0] data4; // 4-bit: exercises width<64 path in maxVal computation + logic [63:0] data64; // 64-bit: exercises width>=64 (UINT64_MAX) path covergroup cg; coverpoint data { bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7] } endgroup + + // 4-bit auto bins: exercises (width < 64) path: maxVal = (1<<4)-1 = 15 + covergroup cg_4bit; + coverpoint data4 { + bins auto[4]; // Creates 4 bins: [0:3], [4:7], [8:11], [12:15] + } + endgroup + + // 4-bit auto bins with ignore_bins: exercises excluded-value skip path + covergroup cg_4bit_excl; + coverpoint data4 { + ignore_bins bad = {0}; // value 0 excluded from auto expansion + bins auto[4]; + } + endgroup + + // auto_bin_max=2 on 64-bit: exercises numTotalValues=UINT64_MAX path + covergroup cg2; + option.auto_bin_max = 2; + coverpoint data64; + endgroup /* verilator lint_on CMPCONST */ initial begin - automatic cg cg_inst = new; + automatic cg cg_inst = new; + automatic cg_4bit cg4_inst = new; + automatic cg_4bit_excl cg4e_inst = new; + automatic cg2 cg2_inst = new; - // Initial coverage should be 0% + // Sample 3-bit cg: one value per bin + data = 0; cg_inst.sample(); + data = 2; cg_inst.sample(); + data = 5; cg_inst.sample(); + data = 7; cg_inst.sample(); - // Sample first bin: 0 or 1 - data = 0; - cg_inst.sample(); + // Sample 4-bit bins + data4 = 0; cg4_inst.sample(); // bin [0:3] + data4 = 7; cg4_inst.sample(); // bin [4:7] + data4 = 10; cg4_inst.sample(); // bin [8:11] + data4 = 14; cg4_inst.sample(); // bin [12:15] - // Sample second bin: 2 or 3 - data = 2; - cg_inst.sample(); + // Sample 4-bit with exclusion (value 0 excluded; bins start at 1) + data4 = 1; cg4e_inst.sample(); + data4 = 8; cg4e_inst.sample(); - // Sample third bin: 4 or 5 - data = 5; - cg_inst.sample(); - - // Sample fourth bin: 6 or 7 - data = 7; - cg_inst.sample(); + // Sample both 64-bit range bins + data64 = 64'd0; cg2_inst.sample(); // auto[0] + data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample(); // auto[1] $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_covergroup_cross.out b/test_regress/t/t_covergroup_cross.out index 3414a344b..536ac7ca5 100644 --- a/test_regress/t/t_covergroup_cross.out +++ b/test_regress/t/t_covergroup_cross.out @@ -49,3 +49,19 @@ cg4.cp_mode.debug: 2 cg4.cp_mode.normal: 2 cg4.cp_parity.even: 2 cg4.cp_parity.odd: 2 +cg5.addr_cmd_opt.addr0_x_read [cross]: 1 +cg5.addr_cmd_opt.addr0_x_write [cross]: 0 +cg5.addr_cmd_opt.addr1_x_read [cross]: 0 +cg5.addr_cmd_opt.addr1_x_write [cross]: 1 +cg5.cp_addr.addr0: 1 +cg5.cp_addr.addr1: 1 +cg5.cp_cmd.read: 1 +cg5.cp_cmd.write: 1 +cg_range.addr_cmd_range.hi_range_x_read [cross]: 1 +cg_range.addr_cmd_range.hi_range_x_write [cross]: 1 +cg_range.addr_cmd_range.lo_range_x_read [cross]: 1 +cg_range.addr_cmd_range.lo_range_x_write [cross]: 1 +cg_range.cp_addr.hi_range: 2 +cg_range.cp_addr.lo_range: 2 +cg_range.cp_cmd.read: 2 +cg_range.cp_cmd.write: 2 diff --git a/test_regress/t/t_covergroup_cross.v b/test_regress/t/t_covergroup_cross.v index 390eb1258..20063da01 100644 --- a/test_regress/t/t_covergroup_cross.v +++ b/test_regress/t/t_covergroup_cross.v @@ -64,9 +64,39 @@ module t; addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity; endgroup + // Cross with option inside body: exercises addOptionsp in visit(AstCoverCross*) + covergroup cg5; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_opt: cross cp_addr, cp_cmd { + option.weight = 2; + } + endgroup + + // 2-way cross with range bin: exercises lo!=hi path in buildBinCondition + covergroup cg_range; + cp_addr: coverpoint addr { + bins lo_range = {[0:1]}; // range bin (lo != hi) -> makeRangeCondition path + bins hi_range = {[2:3]}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_range: cross cp_addr, cp_cmd; + endgroup + cg2 cg2_inst = new; + cg_range cg_range_inst = new; cg3 cg3_inst = new; cg4 cg4_inst = new; + cg5 cg5_inst = new; initial begin // Sample 2-way: hit all 4 combinations @@ -87,6 +117,16 @@ module t; addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample(); addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample(); + // Sample cg5 (cross with option) + addr = 0; cmd = 0; cg5_inst.sample(); + addr = 1; cmd = 1; cg5_inst.sample(); + + // Sample range-bin cross + addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read + addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write + addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write + addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_default_bins.out b/test_regress/t/t_covergroup_default_bins.out index ab12e5a34..ec7fde6f9 100644 --- a/test_regress/t/t_covergroup_default_bins.out +++ b/test_regress/t/t_covergroup_default_bins.out @@ -1,3 +1,4 @@ cg.data.high: 1 cg.data.low: 1 cg.data.other: 2 +cg2.cp_only_default.all: 4 diff --git a/test_regress/t/t_covergroup_default_bins.v b/test_regress/t/t_covergroup_default_bins.v index d4e38ac5e..9b221e56a 100644 --- a/test_regress/t/t_covergroup_default_bins.v +++ b/test_regress/t/t_covergroup_default_bins.v @@ -17,26 +17,39 @@ module t; } endgroup - initial begin - cg cg_inst; + // Covergroup with default as the ONLY bin: exercises defaultCondp=BitTrue path + covergroup cg2; + cp_only_default: coverpoint data { + bins all = default; + } + endgroup - cg_inst = new(); + initial begin + cg cg_inst; + cg2 cg2_inst; + + cg_inst = new(); + cg2_inst = new(); // Hit low bin data = 2; cg_inst.sample(); + cg2_inst.sample(); // Hit high bin data = 14; cg_inst.sample(); + cg2_inst.sample(); // Hit default bin with value 7 (not in low or high) data = 7; cg_inst.sample(); + cg2_inst.sample(); // Hit another default value (should not increase coverage) data = 20; cg_inst.sample(); + cg2_inst.sample(); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_covergroup_iff.out b/test_regress/t/t_covergroup_iff.out index 1f0a24561..5ce6817a0 100644 --- a/test_regress/t/t_covergroup_iff.out +++ b/test_regress/t/t_covergroup_iff.out @@ -1,4 +1,9 @@ +cg_array_iff.cp.arr: 1 +cg_default_iff.cp.def: 1 +cg_default_iff.cp.known: 1 cg_iff.cp_value.disabled_hi: 0 cg_iff.cp_value.disabled_lo: 0 cg_iff.cp_value.enabled_hi: 1 cg_iff.cp_value.enabled_lo: 1 +cg_trans2_iff.cp.t2: 1 +cg_trans3_iff.cp.t3: 1 diff --git a/test_regress/t/t_covergroup_iff.v b/test_regress/t/t_covergroup_iff.v index c3f776c70..256522a08 100644 --- a/test_regress/t/t_covergroup_iff.v +++ b/test_regress/t/t_covergroup_iff.v @@ -6,15 +6,14 @@ // SPDX-License-Identifier: CC0-1.0 // Test iff (enable) guard: sampling is gated by the enable condition. -// Samples taken while enable=0 must not increment bins. -// Bins 'disabled_*' are sampled only with enable=0 -- they must NOT appear in -// coverage.dat. Bins 'enabled_*' are sampled only with enable=1 -- they must -// appear. This makes pass/fail unambiguous from the coverage report alone. +// Covers iff on explicit value bins, default bin, array bins, +// simple 2-step transition, and 3-step transition. module t; logic enable; int value; + // --- Original: iff on explicit value bins (lines 565-567) --- covergroup cg_iff; cp_value: coverpoint value iff (enable) { bins disabled_lo = {1}; @@ -24,18 +23,83 @@ module t; } endgroup - cg_iff cg = new; + // --- iff on default bin (lines 633-635/641) --- + covergroup cg_default_iff; + cp: coverpoint value iff (enable) { + bins known = {10}; + bins def = default; // default bin with coverpoint-level iff + } + endgroup + + // --- iff on array bins (lines 982-983) --- + covergroup cg_array_iff; + cp: coverpoint value iff (enable) { + bins arr[] = {5, 6, 7}; // array bins, all gated by iff + } + endgroup + + // --- iff on 2-step transition (lines 1109-1110) --- + covergroup cg_trans2_iff; + cp: coverpoint value iff (enable) { + bins t2 = (1 => 2); + } + endgroup + + // --- iff on 3-step transition (lines 724-725, 762-763) --- + covergroup cg_trans3_iff; + cp: coverpoint value iff (enable) { + bins t3 = (1 => 2 => 3); + } + endgroup + + cg_iff cg1 = new; + cg_default_iff cg2 = new; + cg_array_iff cg3 = new; + cg_trans2_iff cg4 = new; + cg_trans3_iff cg5 = new; initial begin // Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded enable = 0; - value = 1; cg.sample(); - value = 2; cg.sample(); + value = 1; cg1.sample(); + value = 2; cg1.sample(); // Sample enabled_lo and enabled_hi with enable=1 -- must be recorded enable = 1; - value = 3; cg.sample(); - value = 4; cg.sample(); + value = 3; cg1.sample(); + value = 4; cg1.sample(); + + // cg2: default bin -- enable=1 lets known and default through + enable = 1; + value = 10; cg2.sample(); // hits 'known' + value = 99; cg2.sample(); // hits 'def' (default) + enable = 0; + value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def' + + // cg3: array bins with iff + enable = 1; + value = 5; cg3.sample(); // arr[5] hit + enable = 0; + value = 6; cg3.sample(); // gated + + // cg4: 2-step transition with iff + enable = 1; + value = 1; cg4.sample(); + value = 2; cg4.sample(); // (1=>2) hit with enable=1 + enable = 0; + value = 1; cg4.sample(); + value = 2; cg4.sample(); // (1=>2) gated by iff + + // cg5: 3-step transition with iff + enable = 1; + value = 1; cg5.sample(); + value = 2; cg5.sample(); // mid-sequence, enable=1 + enable = 0; + value = 3; cg5.sample(); // iff fails at step 3 -- triggers restart path (line 762-763) + enable = 1; + value = 1; cg5.sample(); + value = 2; cg5.sample(); + value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1 $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_covergroup_ignore_bins.out b/test_regress/t/t_covergroup_ignore_bins.out index c3e901caa..dc00cfc0b 100644 --- a/test_regress/t/t_covergroup_ignore_bins.out +++ b/test_regress/t/t_covergroup_ignore_bins.out @@ -1,3 +1,12 @@ +cg.data.catch_all [ignore]: 0 cg.data.high: 1 cg.data.low: 1 cg.data.reserved [ignore]: 1 +cg.data.wib: 0 +cg2.cp_auto.auto_0: 1 +cg2.cp_auto.auto_1: 1 +cg2.cp_auto.ign [ignore]: 2 +cg2.cp_auto.ign_trans [ignore]: 1 +cg2.cp_bounds.hi: 2 +cg2.cp_bounds.lo: 2 +cg2.cp_full.all: 4 diff --git a/test_regress/t/t_covergroup_ignore_bins.v b/test_regress/t/t_covergroup_ignore_bins.v index 87bee7b54..fd270beca 100644 --- a/test_regress/t/t_covergroup_ignore_bins.v +++ b/test_regress/t/t_covergroup_ignore_bins.v @@ -8,23 +8,52 @@ module t (/*AUTOARG*/); logic [3:0] data; + logic [1:0] data2; // 2-bit signal for range-boundary tests covergroup cg; coverpoint data { bins low = {[0:3]}; bins high = {[8:11]}; - ignore_bins reserved = {[12:15]}; + ignore_bins reserved = {[12:15]}; + ignore_bins catch_all = default; // null rangesp: exercises generateBinMatchCode !fullCondp + wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore bins (L7084-7085) } endgroup - cg cg_inst; + // Exercises: + // extractValuesFromRange AstInsideRange branch (ignore_bins range, no regular bins) + // createImplicitAutoBins with excluded range values + // makeRangeCondition: skipUpperCheck=true (hi=maxVal) and both-skip (BitTrue) + // ignore_bins with transition list (L7102-7104) + covergroup cg2; + cp_auto: coverpoint data2 { + ignore_bins ign = {[2:3]}; // range ignore, no regular bins -> auto-bins created + ignore_bins ign_trans = (0 => 1); // ignore_bins with transition (L7102-7104) + } + cp_bounds: coverpoint data2 { + bins lo = {[0:1]}; // lo=0: skipLowerCheck -> AstLte + bins hi = {[2:3]}; // hi=maxVal (2-bit): skipUpperCheck -> AstGte + } + cp_full: coverpoint data2 { + bins all = {[0:3]}; // lo=0 and hi=maxVal: both skip -> AstConst(BitTrue) + } + endgroup + + cg cg_inst; + cg2 cg2_inst; initial begin - cg_inst = new; + cg_inst = new; + cg2_inst = new; - data = 13; cg_inst.sample(); // reserved - ignored - data = 1; cg_inst.sample(); // low - data = 10; cg_inst.sample(); // high + data = 13; cg_inst.sample(); // reserved - ignored + data = 1; cg_inst.sample(); // low + data = 10; cg_inst.sample(); // high + + data2 = 0; cg2_inst.sample(); // auto_0, lo, all + data2 = 1; cg2_inst.sample(); // auto_1, lo, all + data2 = 2; cg2_inst.sample(); // ign, hi, all + data2 = 3; cg2_inst.sample(); // ign, hi, all $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_covergroup_illegal_bins.out b/test_regress/t/t_covergroup_illegal_bins.out index dbb5588a3..6355322b5 100644 --- a/test_regress/t/t_covergroup_illegal_bins.out +++ b/test_regress/t/t_covergroup_illegal_bins.out @@ -2,3 +2,10 @@ cg.data.forbidden [illegal]: 0 cg.data.high: 1 cg.data.low: 1 cg.data.mid: 1 +cg2.cp_arr.bad_arr [illegal]: 0 +cg2.cp_arr.ok: 1 +cg2.cp_arr.wlib: 0 +cg2.cp_trans.bad_2step [illegal]: 0 +cg2.cp_trans.bad_3step [illegal]: 0 +cg2.cp_trans.lib_default [illegal]: 0 +cg2.cp_trans.ok: 1 diff --git a/test_regress/t/t_covergroup_illegal_bins.v b/test_regress/t/t_covergroup_illegal_bins.v index bb8f59693..31c837ee6 100644 --- a/test_regress/t/t_covergroup_illegal_bins.v +++ b/test_regress/t/t_covergroup_illegal_bins.v @@ -9,6 +9,7 @@ module t; logic [1:0] data; + logic [3:0] data4; covergroup cg; coverpoint data { @@ -19,18 +20,33 @@ module t; } endgroup + // cg2: exercises illegal_bins with 3-step transition (lines 744-747) and + // illegal_bins array notation (lines 996-998) + covergroup cg2; + cp_trans: coverpoint data4 { + bins ok = {0}; + illegal_bins bad_2step = (1 => 2); // 2-step illegal transition (simple path) + illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition + illegal_bins lib_default = default; // illegal_bins = default (L7123-7124) + } + cp_arr: coverpoint data4 { + bins ok = {0}; + illegal_bins bad_arr[] = {8, 9, 10}; // illegal array bins + wildcard illegal_bins wlib = {4'b1?00}; // wildcard illegal bins (L7087-7088) + } + endgroup + initial begin - automatic cg cg_inst = new; + automatic cg cg_inst = new; + automatic cg2 cg2_inst = new; // Sample legal values only - data = 0; - cg_inst.sample(); + data = 0; cg_inst.sample(); + data = 1; cg_inst.sample(); + data = 2; cg_inst.sample(); - data = 1; - cg_inst.sample(); - - data = 2; - cg_inst.sample(); + // Sample cg2 - only safe values, never triggering illegal bins + data4 = 0; cg2_inst.sample(); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_covergroup_option.v b/test_regress/t/t_covergroup_option.v index 7841414f1..1e8775d5e 100644 --- a/test_regress/t/t_covergroup_option.v +++ b/test_regress/t/t_covergroup_option.v @@ -7,18 +7,57 @@ // Test option.name syntax: both declaration-time and runtime assignment compile. // Note: option.name does not currently affect the coverage.dat hierarchy key; // the type name is used regardless. +// Also tests option.weight, option.goal, option.per_instance, option.comment. module t; // verilator lint_off COVERIGN + logic [3:0] data; + covergroup cg(); option.name = "decl_name"; endgroup - cg cov1; + // Test option.weight, option.goal, option.per_instance, option.comment + // Covergroup-level options (parsed but passed through constructor body) + covergroup cg2; + option.weight = 2; + option.goal = 90; + option.per_instance = 1; + option.comment = "my covergroup"; + cp: coverpoint data; + endgroup + + // Coverpoint-level options: exercises visit(AstCoverpoint*) option-type branches + // in V3LinkParse (weight/goal/per_instance/comment dispatch) + covergroup cg3; + cp: coverpoint data { + option.weight = 2; + option.goal = 90; + option.per_instance = 1; + option.comment = "cp comment"; + bins lo = {[0:7]}; + bins hi = {[8:15]}; + } + endgroup + + cg cov1; + cg2 cov2; + cg3 cov3; initial begin cov1 = new; cov1.option.name = "new_cov1_name"; + + cov2 = new; + data = 5; + cov2.sample(); + + cov3 = new; + data = 3; + cov3.sample(); + data = 10; + cov3.sample(); + $finish; end diff --git a/test_regress/t/t_covergroup_trans.out b/test_regress/t/t_covergroup_trans.out index 40f75ca02..b5907a18e 100644 --- a/test_regress/t/t_covergroup_trans.out +++ b/test_regress/t/t_covergroup_trans.out @@ -1,4 +1,5 @@ cg.cp_array.arr: 3 +cg.cp_multi_item.multi: 1 cg.cp_trans2.trans1: 1 cg.cp_trans2.trans2: 1 cg.cp_trans2.trans3: 1 diff --git a/test_regress/t/t_covergroup_trans.py b/test_regress/t/t_covergroup_trans.py index ceec4c59e..6e9a8f8a2 100644 --- a/test_regress/t/t_covergroup_trans.py +++ b/test_regress/t/t_covergroup_trans.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile(verilator_flags2=['--coverage']) +test.compile(verilator_flags2=['--coverage --Wno-COVERIGN']) test.execute() diff --git a/test_regress/t/t_covergroup_trans.v b/test_regress/t/t_covergroup_trans.v index 664c75e24..4e87080f1 100644 --- a/test_regress/t/t_covergroup_trans.v +++ b/test_regress/t/t_covergroup_trans.v @@ -4,7 +4,8 @@ // SPDX-FileCopyrightText: 2026 Wilson Snyder // SPDX-License-Identifier: CC0-1.0 -// Test transition bins: simple 2-value, 3-value sequences, and array bins +// Test transition bins: simple 2-value, 3-value sequences, array bins, +// and multi-value items in transition steps. module t; logic [2:0] state; @@ -25,6 +26,10 @@ module t; cp_array: coverpoint state { bins arr[] = (0 => 1), (1 => 2), (2 => 3); } + // Multi-value item (comma list) in transition: matches 1 or 2 in second step + cp_multi_item: coverpoint state { + bins multi = (0 => 1, 2); // second element is a two-value list + } endgroup cg cg_inst = new; @@ -32,7 +37,7 @@ module t; initial begin // Drive sequence 0->1->2->3->4 which hits all bins state = 0; cg_inst.sample(); - state = 1; cg_inst.sample(); // 0=>1: trans1, seq_a pos1, arr[0=>1] + state = 1; cg_inst.sample(); // 0=>1: trans1, seq_a pos1, arr[0=>1], multi state = 2; cg_inst.sample(); // 1=>2: trans2, seq_a done, arr[1=>2] state = 3; cg_inst.sample(); // 2=>3: trans3, seq_b pos1, arr[2=>3] state = 4; cg_inst.sample(); // 3=>4: seq_b done diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 835c4d7d6..27e9927a4 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -1,199 +1,244 @@ -%Warning-COVERIGN: t/t_covergroup_unsup.v:64:24: Unsupported: '@@' coverage event - 64 | covergroup cg_atat() @@ (begin funca or end funcb); +%Warning-COVERIGN: t/t_covergroup_unsup.v:65:24: Unsupported: '@@' coverage event + 65 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~ ... 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_unsup.v:98:21: Unsupported: 'iff' in coverage cross - 98 | cross a, b iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:99:21: Unsupported: 'iff' in coverage cross + 99 | cross a, b iff (!rst); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:101:21: Unsupported: 'iff' in coverage cross - 101 | cross a, b iff (!rst) {} +%Warning-COVERIGN: t/t_covergroup_unsup.v:102:21: Unsupported: 'iff' in coverage cross + 102 | cross a, b iff (!rst) {} | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:108:21: Unsupported: 'function' in coverage cross body - 108 | function void crossfunc; endfunction +%Warning-COVERIGN: t/t_covergroup_unsup.v:109:21: Unsupported: 'function' in coverage cross body + 109 | function void crossfunc; endfunction | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:109:18: Unsupported: function call in coverage select expression - 109 | bins one = crossfunc(); +%Warning-COVERIGN: t/t_covergroup_unsup.v:110:18: Unsupported: function call in coverage select expression + 110 | bins one = crossfunc(); | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:109:7: Unsupported: explicit coverage cross bins - 109 | bins one = crossfunc(); +%Warning-COVERIGN: t/t_covergroup_unsup.v:110:7: Unsupported: explicit coverage cross bins + 110 | bins one = crossfunc(); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:113:31: Unsupported: 'iff' in coverage cross - 113 | my_cg_id: cross a, b iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:114:31: Unsupported: 'iff' in coverage cross + 114 | my_cg_id: cross a, b iff (!rst); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:124:14: Unsupported: 'bins' explicit array size (treated as '[]') - 124 | { bins ba[2] = {a}; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:125:14: Unsupported: 'bins' explicit array size (treated as '[]') + 125 | { bins ba[2] = {a}; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:126:21: Unsupported: 'with' in cover bin (bin created without filter) - 126 | { bins ba = {a} with ( b ); } +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:21: Unsupported: 'with' in cover bin (bin created without filter) + 127 | { bins ba = {a} with ( b ); } | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:129:32: Unsupported: 'with' in wildcard cover bin - 129 | { wildcard bins bwaw = {a} with ( b ); } +%Warning-COVERIGN: t/t_covergroup_unsup.v:130:32: Unsupported: 'with' in wildcard cover bin + 130 | { wildcard bins bwaw = {a} with ( b ); } | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:132:27: Unsupported: 'sequence' in default cover bin - 132 | { bins defs = default sequence; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:133:27: Unsupported: 'sequence' in default cover bin + 133 | { bins defs = default sequence; } | ^~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:135:7: Unsupported: 'wildcard' transition list in cover bin - 135 | { wildcard bins wbts = ( 1, 2 ); } +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:7: Unsupported: 'wildcard' transition list in cover bin + 136 | { wildcard bins wbts = ( 1, 2 ); } | ^~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:31: Unsupported: covergroup value range '[...]' - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:31: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:42: Unsupported: covergroup value range '[...]' - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:42: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:57: Unsupported: covergroup value range '[...]' - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:57: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:139:23: Unsupported: '[*]' in cover transition - 139 | { bins bts2 = ( 3 [*5] ) ; } - | ^~ %Warning-COVERIGN: t/t_covergroup_unsup.v:140:23: Unsupported: '[*]' in cover transition - 140 | { bins bts2 = ( 3 [*5:6] ) ; } + 140 | { bins bts2 = ( 3 [*5] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:141:23: Unsupported: '[*]' in cover transition + 141 | { bins bts2 = ( 3 [*5:6] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:141:23: Unsupported: '[->' in cover transition - 141 | { bins bts2 = ( 3 [->5] ) ; } - | ^~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Unsupported: '[->' in cover transition - 142 | { bins bts2 = ( 3 [->5:6] ) ; } + 142 | { bins bts2 = ( 3 [->5] ) ; } + | ^~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Unsupported: '[->' in cover transition + 143 | { bins bts2 = ( 3 [->5:6] ) ; } | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Unsupported: '[=]' in cover transition - 143 | { bins bts2 = ( 3 [=5] ) ; } - | ^~ %Warning-COVERIGN: t/t_covergroup_unsup.v:144:23: Unsupported: '[=]' in cover transition - 144 | { bins bts2 = ( 3 [=5:6] ) ; } + 144 | { bins bts2 = ( 3 [=5] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:149:24: Unsupported: 'with' in cover bin - 149 | bins div_by_2 = a with (item % 2 == 0); +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:23: Unsupported: '[=]' in cover transition + 145 | { bins bts2 = ( 3 [=5:6] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:151:12: Unsupported: 'bins' array (non-auto) + 151 | { bins nonAuto[4]; } + | ^~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:153:35: Unsupported: 'with' in cover bin (bin created without filter) + 153 | { ignore_bins ib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:154:37: Unsupported: 'with' in cover bin (bin created without filter) + 154 | { illegal_bins lib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:156:29: Unsupported: 'with' in cover bin + 156 | { ignore_bins ib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:157:31: Unsupported: 'with' in cover bin + 157 | { illegal_bins lib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:45: Unsupported: 'with' in wildcard cover bin + 159 | { wildcard ignore_bins wib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:47: Unsupported: 'with' in wildcard cover bin + 160 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:162:7: Unsupported: 'wildcard' transition list in cover bin + 162 | { wildcard ignore_bins wib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:163:7: Unsupported: 'wildcard' transition list in cover bin + 163 | { wildcard illegal_bins wlib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:165:40: Unsupported: 'sequence' in default cover bin + 165 | { ignore_bins ib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:166:42: Unsupported: 'sequence' in default cover bin + 166 | { illegal_bins lib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:24: Unsupported: 'with' in cover bin + 171 | bins div_by_2 = a with (item % 2 == 0); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:150:32: Unsupported: 'with' in cover bin - 150 | bins div_by_2_paren[] = a with (item % 2 == 0); +%Warning-COVERIGN: t/t_covergroup_unsup.v:172:32: Unsupported: 'with' in cover bin + 172 | bins div_by_2_paren[] = a with (item % 2 == 0); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:156:20: Unsupported: 'binsof' in coverage select expression - 156 | bins bin_a = binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:20: Unsupported: 'binsof' in coverage select expression + 178 | bins bin_a = binsof(a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:156:7: Unsupported: explicit coverage cross bins - 156 | bins bin_a = binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Unsupported: explicit coverage cross bins + 178 | bins bin_a = binsof(a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:21: Unsupported: 'binsof' in coverage select expression - 157 | bins bin_ai = binsof(a) iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:21: Unsupported: 'binsof' in coverage select expression + 179 | bins bin_ai = binsof(a) iff (!rst); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:7: Unsupported: explicit coverage cross bins - 157 | bins bin_ai = binsof(a) iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:7: Unsupported: explicit coverage cross bins + 179 | bins bin_ai = binsof(a) iff (!rst); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:158:20: Unsupported: 'binsof' in coverage select expression - 158 | bins bin_c = binsof(cp.x); +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:20: Unsupported: 'binsof' in coverage select expression + 180 | bins bin_c = binsof(cp.x); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:158:7: Unsupported: explicit coverage cross bins - 158 | bins bin_c = binsof(cp.x); +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:7: Unsupported: explicit coverage cross bins + 180 | bins bin_c = binsof(cp.x); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:159:21: Unsupported: 'binsof' in coverage select expression - 159 | bins bin_na = ! binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:21: Unsupported: 'binsof' in coverage select expression + 181 | bins bin_na = ! binsof(a); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:159:7: Unsupported: explicit coverage cross bins - 159 | bins bin_na = ! binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:7: Unsupported: explicit coverage cross bins + 181 | bins bin_na = ! binsof(a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:161:30: Unsupported: 'intersect' in coverage select expression - 161 | bins bin_d = binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:30: Unsupported: 'intersect' in coverage select expression + 183 | bins bin_d = binsof(a) intersect { b }; | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:161:7: Unsupported: explicit coverage cross bins - 161 | bins bin_d = binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:7: Unsupported: explicit coverage cross bins + 183 | bins bin_d = binsof(a) intersect { b }; | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:162:31: Unsupported: 'intersect' in coverage select expression - 162 | bins bin_nd = ! binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:31: Unsupported: 'intersect' in coverage select expression + 184 | bins bin_nd = ! binsof(a) intersect { b }; | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:162:7: Unsupported: explicit coverage cross bins - 162 | bins bin_nd = ! binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:7: Unsupported: explicit coverage cross bins + 184 | bins bin_nd = ! binsof(a) intersect { b }; | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:20: Unsupported: 'with' in coverage select expression - 164 | bins bin_e = with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:20: Unsupported: 'with' in coverage select expression + 186 | bins bin_e = with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:7: Unsupported: explicit coverage cross bins - 164 | bins bin_e = with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:7: Unsupported: explicit coverage cross bins + 186 | bins bin_e = with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:165:24: Unsupported: 'with' in coverage select expression - 165 | bins bin_not_e = ! with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:24: Unsupported: 'with' in coverage select expression + 187 | bins bin_not_e = ! with (a); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:165:7: Unsupported: explicit coverage cross bins - 165 | bins bin_not_e = ! with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:7: Unsupported: explicit coverage cross bins + 187 | bins bin_not_e = ! with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:167:23: Unsupported: 'binsof' in coverage select expression - 167 | bins bin_par = (binsof(a)); +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:23: Unsupported: 'binsof' in coverage select expression + 189 | bins bin_par = (binsof(a)); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:167:7: Unsupported: explicit coverage cross bins - 167 | bins bin_par = (binsof(a)); +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:7: Unsupported: explicit coverage cross bins + 189 | bins bin_par = (binsof(a)); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:22: Unsupported: 'binsof' in coverage select expression - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:22: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:35: Unsupported: 'binsof' in coverage select expression - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:35: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:32: Unsupported: '&&' in coverage select expression - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:32: Unsupported: '&&' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:7: Unsupported: explicit coverage cross bins - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:7: Unsupported: explicit coverage cross bins + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:21: Unsupported: 'binsof' in coverage select expression - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:21: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:34: Unsupported: 'binsof' in coverage select expression - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:34: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:31: Unsupported: '||' in coverage select expression - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:31: Unsupported: '||' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:7: Unsupported: explicit coverage cross bins - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:7: Unsupported: explicit coverage cross bins + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:23: Unsupported: 'binsof' in coverage select expression - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:23: Unsupported: 'binsof' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:33: Unsupported: 'with' in coverage select expression - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:33: Unsupported: 'with' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:7: Unsupported: explicit coverage cross bins - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:7: Unsupported: explicit coverage cross bins + 192 | bins bin_with = binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:26: Unsupported: 'binsof' in coverage select expression - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:26: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:39: Unsupported: 'binsof' in coverage select expression - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:39: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:49: Unsupported: 'with' in coverage select expression - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:49: Unsupported: 'with' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:36: Unsupported: '||' in coverage select expression - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:36: Unsupported: '||' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Unsupported: explicit coverage cross bins - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:7: Unsupported: explicit coverage cross bins + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:27: Unsupported: 'binsof' in coverage select expression - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:27: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:40: Unsupported: 'binsof' in coverage select expression - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:40: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:50: Unsupported: 'with' in coverage select expression - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:50: Unsupported: 'with' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:37: Unsupported: '&&' in coverage select expression - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:37: Unsupported: '&&' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:7: Unsupported: explicit coverage cross bins - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:7: Unsupported: explicit coverage cross bins + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:173:34: Unsupported: 'binsof' in coverage select expression - 173 | bins bin_multiple_fields = binsof(p.inner_packet.field); +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:34: Unsupported: 'binsof' in coverage select expression + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:173:7: Unsupported: explicit coverage cross bins - 173 | bins bin_multiple_fields = binsof(p.inner_packet.field); +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:7: Unsupported: explicit coverage cross bins + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:195:5: Unsupported: covergroup inheritance (extends) - 195 | covergroup extends cg_empty; +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:30: Unsupported: 'binsof' in coverage select expression + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:7: Unsupported: explicit coverage cross bins + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:32: Unsupported: 'binsof' in coverage select expression + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:7: Unsupported: explicit coverage cross bins + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:220:5: Unsupported: covergroup inheritance (extends) + 220 | covergroup extends cg_empty; | ^~~~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index eaeb7aa21..15d237213 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -51,6 +51,7 @@ module t ( option.comment = "option_comment"; // cg, cp, cross option.at_least = 20; // cg, cp, cross option.auto_bin_max = 10; // cg, cp + type_option.auto_bin_max = 10; // cg, cp: typeOption() == true option.cross_num_print_missing = 2; // cg, cross option.detect_overlap = 1; // cg, cp option.per_instance = 1; // cg @@ -144,6 +145,27 @@ module t ( { bins bts2 = ( 3 [=5:6] ) ; } endgroup + // Additional bins syntax for grammar coverage (all generate COVERIGN warnings) + covergroup cg_bins_ext; + // Non-auto bins array without value: bins name[N] (no = {value}) -- L7049-7051 + { bins nonAuto[4]; } + // ignore_bins/illegal_bins with 'with' filter on range list -- L7067-7073 + { ignore_bins ib_with = {1,2} with ( b ); } + { illegal_bins lib_with = {1,2} with ( b ); } + // ignore_bins/illegal_bins with 'with' filter on coverpoint ref -- L7077,L7079 + { ignore_bins ib_cp = a with ( b ); } + { illegal_bins lib_cp = a with ( b ); } + // wildcard ignore/illegal bins with 'with' filter -- L7092,L7094 + { wildcard ignore_bins wib_with = {1,2} with ( b ); } + { wildcard illegal_bins wlib_with = {1,2} with ( b ); } + // wildcard ignore/illegal bins with transition list -- L7113,L7114 + { wildcard ignore_bins wib_trans = ( 1 => 2 ); } + { wildcard illegal_bins wlib_trans = ( 1 => 2 ); } + // ignore/illegal bins = default sequence -- L7128,L7130 + { ignore_bins ib_def_seq = default sequence; } + { illegal_bins lib_def_seq = default sequence; } + endgroup + covergroup cg_coverpoint_ref; coverpoint a { bins div_by_2 = a with (item % 2 == 0); @@ -171,6 +193,9 @@ module t ( bins bin_or_with = binsof(a) || binsof(a) with (a); bins bin_and_with = binsof(a) && binsof(a) with (a); bins bin_multiple_fields = binsof(p.inner_packet.field); + // explicit cross ignore/illegal bins (unsupported) -- L7253, L7255 + ignore_bins ib_cross = binsof(a); + illegal_bins lib_cross = binsof(a); } endgroup diff --git a/test_regress/t/t_covergroup_wildcard_bins.out b/test_regress/t/t_covergroup_wildcard_bins.out index 00766a777..6cedb321a 100644 --- a/test_regress/t/t_covergroup_wildcard_bins.out +++ b/test_regress/t/t_covergroup_wildcard_bins.out @@ -1,3 +1,5 @@ cg.data.high: 1 -cg.data.low: 1 +cg.data.low: 2 +cg.data.mid_range: 1 cg.data.pattern: 2 +cg.data.wc_point: 2 diff --git a/test_regress/t/t_covergroup_wildcard_bins.v b/test_regress/t/t_covergroup_wildcard_bins.v index 625c003f6..e4101b33d 100644 --- a/test_regress/t/t_covergroup_wildcard_bins.v +++ b/test_regress/t/t_covergroup_wildcard_bins.v @@ -19,6 +19,12 @@ module t; // Match specific pattern with don't cares wildcard bins pattern = {8'b10?0_11??}; + + // Non-wildcard range bin: InsideRange [min:max] where min != max (line 1318) + bins mid_range = {[8'h40 : 8'h4F]}; + + // Wildcard bin using single-value InsideRange [5:5] (min==max, line 1312) + wildcard bins wc_point = {[8'd5 : 8'd5]}; } endgroup @@ -43,6 +49,14 @@ module t; data = 8'b1010_1111; // Should also match 'pattern' (10[1]0_11[1]1) cg_inst.sample(); + // Test mid_range bin: [0x40:0x4F] + data = 8'h45; // Should match 'mid_range' + cg_inst.sample(); + + // Test wc_point bin: exact value 5 + data = 8'd5; // Should match 'wc_point' + cg_inst.sample(); + // Verify non-matching value doesn't change coverage data = 8'b0101_0101; // Shouldn't match any bin cg_inst.sample(); diff --git a/test_regress/t/t_vlcov_covergroup.annotate.out b/test_regress/t/t_vlcov_covergroup.annotate.out index 38ff6dfcb..831347177 100644 --- a/test_regress/t/t_vlcov_covergroup.annotate.out +++ b/test_regress/t/t_vlcov_covergroup.annotate.out @@ -65,9 +65,39 @@ %000001 addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity; endgroup + // Cross with option inside body: exercises addOptionsp in visit(AstCoverCross*) +%000002 covergroup cg5; +%000001 cp_addr: coverpoint addr { +%000001 bins addr0 = {0}; +%000001 bins addr1 = {1}; + } +%000001 cp_cmd: coverpoint cmd { +%000001 bins read = {0}; +%000001 bins write = {1}; + } +%000001 addr_cmd_opt: cross cp_addr, cp_cmd { + option.weight = 2; + } + endgroup + + // 2-way cross with range bin: exercises lo!=hi path in buildBinCondition +%000004 covergroup cg_range; +%000001 cp_addr: coverpoint addr { +%000002 bins lo_range = {[0:1]}; // range bin (lo != hi) -> makeRangeCondition path +%000002 bins hi_range = {[2:3]}; + } +%000001 cp_cmd: coverpoint cmd { +%000002 bins read = {0}; +%000002 bins write = {1}; + } +%000001 addr_cmd_range: cross cp_addr, cp_cmd; + endgroup + %000001 cg2 cg2_inst = new; +%000001 cg_range cg_range_inst = new; %000001 cg3 cg3_inst = new; %000001 cg4 cg4_inst = new; +%000001 cg5 cg5_inst = new; %000001 initial begin // Sample 2-way: hit all 4 combinations @@ -88,6 +118,16 @@ %000001 addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample(); %000001 addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample(); + // Sample cg5 (cross with option) +%000001 addr = 0; cmd = 0; cg5_inst.sample(); +%000001 addr = 1; cmd = 1; cg5_inst.sample(); + + // Sample range-bin cross +%000001 addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read +%000001 addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write +%000001 addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write +%000001 addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read + %000001 $write("*-* All Finished *-*\n"); %000001 $finish; end diff --git a/test_regress/t/t_vlcov_covergroup.out b/test_regress/t/t_vlcov_covergroup.out index 0ec7fa5b7..86d8314f4 100644 --- a/test_regress/t/t_vlcov_covergroup.out +++ b/test_regress/t/t_vlcov_covergroup.out @@ -1,7 +1,7 @@ COVERGROUP COVERAGE REPORT ========================== -TOTAL: 31/51 bins covered (60.78%) +TOTAL: 45/67 bins covered (67.16%) ------------------------------------------------------------------------------ Covergroup Type: cg2 [t/t_covergroup_cross.v:18] @@ -115,3 +115,51 @@ Covergroup Type: cg4 [t/t_covergroup_cross.v:49] COVERED addr1_x_write_x_normal_x_odd 1 hits ------------------------------------------------------------------------------ +Covergroup Type: cg5 [t/t_covergroup_cross.v:70] + Type Coverage: 6/8 bins (75.00%) + + Coverpoint: cp_addr + Coverage: 2/2 bins (100.00%) + Bins: + COVERED addr0 1 hits + COVERED addr1 1 hits + + Coverpoint: cp_cmd + Coverage: 2/2 bins (100.00%) + Bins: + COVERED read 1 hits + COVERED write 1 hits + + Cross: addr_cmd_opt + Coverage: 2/4 bins (50.00%) + Bins: + COVERED addr0_x_read 1 hits + ZERO addr0_x_write 0 hits + ZERO addr1_x_read 0 hits + COVERED addr1_x_write 1 hits + +------------------------------------------------------------------------------ +Covergroup Type: cg_range [t/t_covergroup_cross.v:85] + Type Coverage: 8/8 bins (100.00%) + + Coverpoint: cp_addr + Coverage: 2/2 bins (100.00%) + Bins: + COVERED lo_range 2 hits + COVERED hi_range 2 hits + + Coverpoint: cp_cmd + Coverage: 2/2 bins (100.00%) + Bins: + COVERED read 2 hits + COVERED write 2 hits + + Cross: addr_cmd_range + Coverage: 4/4 bins (100.00%) + Bins: + COVERED hi_range_x_read 1 hits + COVERED hi_range_x_write 1 hits + COVERED lo_range_x_read 1 hits + COVERED lo_range_x_write 1 hits + +------------------------------------------------------------------------------