Checkpoint on closing coverage holes

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
This commit is contained in:
Matthew Ballance 2026-03-27 21:27:08 +00:00
parent d5f8790d1c
commit f1389497c1
34 changed files with 796 additions and 455 deletions

View File

@ -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

View File

@ -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)

View File

@ -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); }

View File

@ -63,10 +63,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
// Track coverpoints that need previous value tracking (for transition bins)
std::map<AstCoverpoint*, AstVar*> 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<AstCoverBin*, AstVar*> 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<uint64_t>& 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<uint64_t>& 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<AstCoverBin*> 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<AstNodeExpr*> 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<AstCoverBin*> 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");

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -1,2 +1,4 @@
cg.data.grouped: 2
cg.data.values: 3
cg2.cp.range_arr: 3
cg3.cp.range_sized: 3

View File

@ -11,7 +11,7 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.compile(verilator_flags2=['--coverage --Wno-COVERIGN'])
test.execute()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,4 @@
cg.data.high: 1
cg.data.low: 1
cg.data.other: 2
cg2.cp_only_default.all: 4

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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
------------------------------------------------------------------------------