From 4ab229e659451a5bebf79d2e4600b96d410e141c Mon Sep 17 00:00:00 2001 From: Marco Bartoli Date: Mon, 28 Sep 2026 03:06:09 +0200 Subject: [PATCH] Support wildcard bins (#8516) --- src/V3AstAttr.h | 6 + src/V3Covergroup.cpp | 338 +++++++++++++++--- src/V3EmitV.cpp | 7 +- src/verilog.y | 6 +- test_regress/t/t_covergroup_autobins_bad.out | 8 + test_regress/t/t_covergroup_autobins_bad.v | 11 + test_regress/t/t_covergroup_cross.out | 9 +- .../t/t_covergroup_exclusions_unsup.out | 50 ++- .../t/t_covergroup_exclusions_unsup.v | 18 + .../t/t_covergroup_max_bins_unsup.out | 22 +- test_regress/t/t_covergroup_max_bins_unsup.v | 8 + test_regress/t/t_covergroup_wild_real_bad.out | 7 + test_regress/t/t_covergroup_wild_real_bad.v | 3 + test_regress/t/t_covergroup_wildcard_bins.out | 141 ++++++++ test_regress/t/t_covergroup_wildcard_bins.v | 141 ++++++++ .../t/t_covergroup_wildcard_max_bins.out | 8 + .../t/t_covergroup_wildcard_max_bins.py | 15 + .../t/t_covergroup_wildcard_max_bins.v | 62 ++++ ...t_covergroup_wildcard_max_bins_illegal.out | 3 + .../t_covergroup_wildcard_max_bins_illegal.py | 24 ++ ...rgroup_wildcard_max_bins_illegal_sized.out | 3 + ...ergroup_wildcard_max_bins_illegal_sized.py | 24 ++ test_regress/t/t_debug_emitv.out | 1 + test_regress/t/t_debug_emitv.v | 1 + .../t/t_vlcov_covergroup.annotate.out | 19 +- 25 files changed, 849 insertions(+), 86 deletions(-) create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins.out create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins.py create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins.v create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins_illegal.out create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins_illegal.py create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out create mode 100644 test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.py diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index 508ad640a..5c299aa46 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -1212,6 +1212,12 @@ public: "illegal_bins", "transition", "bins", "wildcard"}; return names[m_e]; } + // Keyword declaring the bins, without any 'wildcard' prefix + const char* verilogKwd() const { + static const char* const names[] = {"bins", "bins", "bins", "bins", "ignore_bins", + "illegal_bins", "bins", "bins", "bins"}; + return names[m_e]; + } // VlCovBinKind enumerator naming the bin's set const char* binSetEnum() const { switch (m_e) { diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 09c320a3c..da431905f 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -228,7 +228,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { // NODE STATE // Entire netlist: // AstCoverpoint::user1p() -> AstVar*. Previous-value variable for transition bins + // AstCoverpoint::user2() -> bool. Had a bins declaration ignored, so no automatic bins const VNUser1InUse m_inuser1; + const VNUser2InUse m_inuser2; // STATE std::set @@ -266,6 +268,10 @@ class FunctionalCoverageVisitor final : public VNVisitor { std::vector runs; // Runs of an array or automatic bins declaration, in order uint32_t count = 0; // Bins across all runs bool unsupported = false; // Too many bins, or invalid: the declaration is ignored + // The value of each bin, which names it, of a wildcard array; else the bins are indexed + std::vector values; + // Too many values of an ignore or illegal wildcard array, which is then one bin + bool single = false; }; struct CrossBinValues final { AstCoverBin* binp; // Declaration owning this Normal bin @@ -458,14 +464,6 @@ class FunctionalCoverageVisitor final : public VNVisitor { generateCoverageComputationCode(); } - // Limit on the bins of one array or automatic bins declaration of an integral coverpoint, - // and on the automatic bins of option.auto_bin_max: --coverage-max-bins, like other - // simulators' limits. It guards against hangs from e.g. signed underflow. Such bins - // generate as runs, whose code size does not depend on their number. - static uint32_t binsLimit() { return v3Global.opt.coverageMaxBins(); } - // Limit on the values of one array bins declaration of a real coverpoint, which generates a - // comparison per value (see extractArrayValues): --coverage-max-real-bins - static uint32_t realBinsLimit() { return v3Global.opt.coverageMaxRealBins(); } static constexpr size_t VALUE_LIST_ENTRIES = 256; // Metadata entries per constructor call // The number of bins a constant array size requests: -1 if it is negative, and saturated @@ -481,7 +479,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { const AstConst* const constp = VN_CAST(binp->arraySizep(), Const); if (!constp) return 0; const int64_t count = binsCount(constp); - return count < 1 || count > binsLimit() ? 0 : static_cast(count); + return count < 1 || count > v3Global.opt.coverageMaxBins() ? 0 + : static_cast(count); } // True for a 'bins auto[N]' declaration, or the implicit automatic bins of a coverpoint @@ -497,7 +496,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { } // Check the sized arrays of bins of a coverpoint, dropping invalid ones. A real - // coverpoint's are unsupported, and treated as arrays of a bin per value. + // coverpoint's are unsupported, and treated as arrays of a bin per value. A wildcard array + // of too many ranges of values is ignored, or if ignore or illegal, treated as one bin. void checkSizedArrays(AstCoverpoint* coverpointp) { const bool integral = coverpointp->exprp()->dtypep()->skipRefp()->isIntegralOrPacked(); for (AstNode* nodep = coverpointp->binsp(); nodep;) { @@ -520,6 +520,25 @@ class FunctionalCoverageVisitor final : public VNVisitor { "coverpoint (treated as '[]')"); VL_DO_DANGLING(pushDeletep(sizep->unlinkFrBack()), sizep); continue; + } else if (binp->isWildcard() + && sizedWildcardRuns(binp, coverpointp->exprp()) + > v3Global.opt.coverageMaxBins()) { + // An ignore or illegal array still excludes or checks its values, as one bin + const bool single = !binp->binsType().binIsNormal(); + binp->v3warn(COVERIGN, + "Unsupported: sized wildcard array '" + << binp->binsType().verilogKwd() + << "' of more than --coverage-max-bins of " + << v3Global.opt.coverageMaxBins() << " ranges of values; " + << (single ? "treated as one bin" : "bin ignored") << "\n" + << binp->warnMore() + << "... Suggest a larger --coverage-max-bins"); + if (single) { + VL_DO_DANGLING(pushDeletep(sizep->unlinkFrBack()), sizep); + binp->isArray(false); + continue; + } + coverpointp->user2(true); } else { continue; } @@ -527,6 +546,20 @@ class FunctionalCoverageVisitor final : public VNVisitor { } } + // The ranges of values the wildcard patterns of a sized wildcard array give, counted up to + // more than --coverage-max-bins + static size_t sizedWildcardRuns(const AstCoverBin* binp, AstNodeExpr* exprp) { + std::vector> runs; + for (AstNode* rangep = binp->rangesp(); rangep; rangep = rangep->nextp()) { + if (!VN_IS(rangep, Const)) continue; // A range, or a value known at construction + CrossValueRange range{rangep, resolveWidth(rangep, exprp)}; + if (resolveValue(rangep, exprp, true, true, range) && !crossRangeEmpty(range)) { + crossRangeRuns(range, v3Global.opt.coverageMaxBins(), runs); + } + } + return runs.size(); + } + // Check the automatic bins declarations of a coverpoint. Each stays one declaration, which // generates as a partition of the coverpoint domain (see autoBinRuns). void checkAutomaticBins(AstCoverpoint* coverpointp, const AstNodeExpr* exprp) { @@ -539,10 +572,10 @@ class FunctionalCoverageVisitor final : public VNVisitor { } else if (binsCount(constp) < 1) { cbinp->v3error("Automatic bins array size must be >= 1, got " << constp->num().toDecimalS()); - } else if (binsCount(constp) > binsLimit()) { + } else if (binsCount(constp) > v3Global.opt.coverageMaxBins()) { cbinp->v3error("Automatic bins array size of " - << constp->num().toDecimalU() << " exceeds limit of " << binsLimit() - << '\n' + << constp->num().toDecimalU() << " exceeds limit of " + << v3Global.opt.coverageMaxBins() << '\n' << cbinp->warnMore() << "... Suggest a larger --coverage-max-bins"); } else if (!exprp->dtypep()->skipRefp()->isIntegralOrPacked()) { cbinp->v3error("Automatic bins are not allowed on a coverpoint of a non-integral " @@ -607,6 +640,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { // IEEE 1800-2023 19.5.3/19.11.1: partition first, then apply exclusions. The partition is one // automatic bins declaration, generated as a run like 'bins auto[N]' but numbering its bins. void createImplicitAutoBins(AstCoverpoint* coverpointp, AstNodeExpr* exprp, int autoBinMax) { + if (coverpointp->user2()) return; // Declared bins, ignored, leave no bins for (AstNode* nodep = coverpointp->binsp(); nodep; nodep = nodep->nextp()) { const VCoverBinsType kind = VN_AS(nodep, CoverBin)->binsType(); if (kind != VCoverBinsType::BINS_IGNORE && kind != VCoverBinsType::BINS_ILLEGAL) @@ -626,15 +660,15 @@ class FunctionalCoverageVisitor final : public VNVisitor { "(IEEE 1800-2023 19.5.3)."); return; } - if (count > binsLimit()) { + if (count > v3Global.opt.coverageMaxBins()) { coverpointp->v3warn(COVERIGN, "Unsupported: more than " - << binsLimit() + << v3Global.opt.coverageMaxBins() << " automatic bins from 'option.auto_bin_max'; " "using " - << binsLimit() << ".\n" + << v3Global.opt.coverageMaxBins() << ".\n" << coverpointp->warnMore() << "... Suggest a larger --coverage-max-bins"); - count = binsLimit(); + count = v3Global.opt.coverageMaxBins(); } FileLine* const fl = coverpointp->fileline(); coverpointp->addBinsp(new AstCoverBin{fl, "auto", new AstConst{fl, count}, @@ -808,6 +842,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { for (AstNode* nodep = coverpointp->binsp(); nodep; nodep = nodep->nextp()) { const AstCoverBin* const binp = VN_AS(nodep, CoverBin); if (!binp->binsType().binIsNormal() || binp->transp() || !binp->rangesp()) continue; + // A wildcard array has a bin for each value its elements match, so none empty + if (binp->isArray() && binp->isWildcard()) continue; bool empty = true; for (AstNode* valuep = binp->rangesp(); valuep; valuep = valuep->nextp()) { if (binp->isArray() && VN_IS(valuep, InsideRange)) continue; @@ -965,6 +1001,20 @@ class FunctionalCoverageVisitor final : public VNVisitor { bins.push_back({{0, maxVal}}); return exprp->width() <= 64; } + if (cbinp->isArray() && cbinp->isWildcard() && !cbinp->arraySizep()) { + // A value is in at most one bin of a wildcard array: one slot covering its values. + // Signed values are sign-extended, not unsigned intervals (see computeHitListBound) + ++slotCount; + if (exprp->isSigned() || exprp->width() > 64) return false; + const BinRuns runs = wildcardBinRuns(cbinp, exprp, false); + if (runs.unsupported) return false; + std::vector> ivs; + for (const BinRun& run : runs.runs) { + ivs.emplace_back(run.m_lo.toUQuad(), run.m_hi.toUQuad()); + } + bins.push_back(std::move(ivs)); + return true; + } if (cbinp->isArray()) return appendArrayBinCrossSlots(cbinp, exprp, bins, slotCount); // Non-array bin: one slot covering the union of its intervals. ++slotCount; @@ -983,8 +1033,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { bool appendArrayBinCrossSlots(AstCoverBin* cbinp, AstNodeExpr* exprp, std::vector>>& bins, int& slotCount) { - // Signed values resolve sign-extended, not as unsigned intervals - bool exact = !exprp->isSigned(); + // Signed values resolve sign-extended, not as unsigned intervals, and wildcard patterns + // are not intervals + bool exact = !exprp->isSigned() && !cbinp->isWildcard(); for (AstNode* rp = cbinp->rangesp(); rp; rp = rp->nextp()) { ++slotCount; RangeBounds rb; @@ -1094,25 +1145,31 @@ class FunctionalCoverageVisitor final : public VNVisitor { } // Check that an element of an array bin (bins b[] = {values/ranges}) is a two-state - // constant value or range; false if not, after reporting it. - static bool checkArrayBinElement(AstCoverBin* arrayBinp, AstNode* rangep) { + // constant value or range; false if not, after reporting it if 'report'. + static bool checkArrayBinElement(AstCoverBin* arrayBinp, AstNode* rangep, bool report = true) { if (const AstInsideRange* const irp = VN_CAST(rangep, InsideRange)) { const AstConst* const minp = VN_CAST(irp->lhsp(), Const); const AstConst* const maxp = VN_CAST(irp->rhsp(), Const); if ((!minp && !VN_IS(irp->lhsp(), Unbounded)) || (!maxp && !VN_IS(irp->rhsp(), Unbounded))) { - arrayBinp->v3error("Non-constant expression in array bins range; " - "range bounds must be constants (IEEE 1800-2023 19.5)"); + if (report) { + arrayBinp->v3error("Non-constant expression in array bins range; " + "range bounds must be constants (IEEE 1800-2023 19.5)"); + } return false; } if ((minp && minp->num().isFourState()) || (maxp && maxp->num().isFourState())) { - arrayBinp->v3error("Four-state (x/z) value in array bins range bound; " - "range bounds must be two-state constants"); + if (report) { + arrayBinp->v3error("Four-state (x/z) value in array bins range bound; " + "range bounds must be two-state constants"); + } return false; } } else if (!VN_IS(rangep, Const)) { - arrayBinp->v3error("Non-constant expression in array bins value list; " - "values must be constants (IEEE 1800-2023 19.5)"); + if (report) { + arrayBinp->v3error("Non-constant expression in array bins value list; " + "values must be constants (IEEE 1800-2023 19.5)"); + } return false; } return true; @@ -1141,11 +1198,13 @@ class FunctionalCoverageVisitor final : public VNVisitor { if (hi < lo) continue; // empty range contributes no bins // Guard against a '$'-bounded or otherwise huge range exploding the bin count. const uint64_t span = hi - lo; // == valueCount - 1 (no overflow: hi >= lo) - if (span >= realBinsLimit() || values.size() + span + 1 > realBinsLimit()) { + if (span >= v3Global.opt.coverageMaxRealBins() + || values.size() + span + 1 > v3Global.opt.coverageMaxRealBins()) { arrayBinp->v3warn(COVERIGN, "Unsupported: array 'bins' of a real coverpoint " "covering more than " - << realBinsLimit() << " values; bin ignored.\n" + << v3Global.opt.coverageMaxRealBins() + << " values; bin ignored.\n" << arrayBinp->warnMore() << "... Suggest a larger --coverage-max-real-bins"); unsupportedOut = true; @@ -1222,9 +1281,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Wider spans exceed any limit count = span.mostSetBitP1() > 32 ? UINT64_MAX : span.toUQuad() + 1; } - if (count > binsLimit() - out.count) { + if (count > v3Global.opt.coverageMaxBins() - out.count) { arrayBinp->v3warn(COVERIGN, "Unsupported: array 'bins' covering more than " - << binsLimit() + << v3Global.opt.coverageMaxBins() << " values (e.g. an open '[lo:$]' range over " "a wide coverpoint); bin ignored\n" << arrayBinp->warnMore() @@ -1241,13 +1300,123 @@ class FunctionalCoverageVisitor final : public VNVisitor { return out; } + // The bins of a wildcard array (wildcard bins b[] = {...}): one for each coverpoint value + // an element matches (IEEE 1800-2023 19.5.4, 19.5.7), in value order, and named by the value + // (19.5.1), as runs of single-value bins. Errors on a non-constant element. More than + // --coverage-max-bins values are unsupported -- emits COVERIGN, and sets unsupported, and for + // an ignore or illegal array, single. 'report' false omits these diagnostics. + static BinRuns wildcardBinRuns(AstCoverBin* arrayBinp, AstNodeExpr* exprp, bool report) { + BinRuns out; + const int width = runWidth(exprp); + const V3Number one{arrayBinp, width, 1}; + // Disjoint runs of the values, in value order, each value once + std::vector> spans; + uint64_t count = 0; // Values of 'spans' + for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) { + rangep = V3Const::constifyEdit(rangep); + if (!checkArrayBinElement(arrayBinp, rangep, report)) { + out.unsupported = true; + return out; + } + CrossValueRange range{rangep, resolveWidth(rangep, exprp)}; + if (!resolveValue(rangep, exprp, true, true, range)) { + if (report) { + rangep->v3warn(E_UNSUPPORTED, "Unsupported: non-integral value in a " + "coverage bin of an integral coverpoint."); + } + continue; + } + if (crossRangeEmpty(range)) continue; + std::vector> found; + crossRangeRuns(range, v3Global.opt.coverageMaxBins(), found); + for (const std::pair& run : found) { + // Coverpoint values, sign-extended in both widths + spans.emplace_back(V3Number{rangep, width, run.first}, + V3Number{rangep, width, run.second}); + } + std::sort(spans.begin(), spans.end(), [](const auto& lhs, const auto& rhs) { + return crossValueLess(lhs.first, rhs.first); + }); + std::vector> merged; + for (const std::pair& span : spans) { + // Adjacent or overlapping values join a run; 'first - 1' cannot overflow + V3Number before{rangep, width}; + before.opSub(span.first, one); + if (merged.empty() || crossValueLess(merged.back().second, before)) { + merged.push_back(span); + } else if (crossValueLess(merged.back().second, span.second)) { + merged.back().second = span.second; + } + } + count = 0; + for (const std::pair& span : merged) { + V3Number size{rangep, width}; + size.opSub(span.second, span.first); + // Beyond 2^32 values exceed any limit + count += size.mostSetBitP1() > 32 ? uint64_t{1} << 33 : size.toUQuad() + 1; + if (count > v3Global.opt.coverageMaxBins()) break; + } + spans = std::move(merged); + if (count > v3Global.opt.coverageMaxBins()) { + // An ignore or illegal array still excludes or checks its values, as one bin + out.single = !arrayBinp->binsType().binIsNormal(); + if (report) { + arrayBinp->v3warn(COVERIGN, + "Unsupported: wildcard array '" + << arrayBinp->binsType().verilogKwd() + << "' of more than --coverage-max-bins of " + << v3Global.opt.coverageMaxBins() << " values; " + << (out.single ? "treated as one bin" : "bin ignored") + << "\n" + << arrayBinp->warnMore() + << "... Suggest a larger --coverage-max-bins"); + } + out.unsupported = true; + return out; + } + } + for (const std::pair& span : spans) { + out.runs.emplace_back(arrayBinp, width, 0); + BinRun& run = out.runs.back(); + run.m_lo = span.first; + run.m_hi = span.second; + // A bin for each value, named by it in the coverpoint's type + V3Number value = span.first; + while (true) { + const V3Number typed{arrayBinp, exprp->width(), value}; + out.values.push_back(exprp->isSigned() ? typed.toDecimalS() : typed.toDecimalU()); + ++run.m_count; + if (value.isCaseEq(span.second)) break; + V3Number next{arrayBinp, width}; + value = next.opAdd(value, one); + } + } + out.count = static_cast(count); + return out; + } + // The runs of an automatic bins declaration, or of an array bin of an integral coverpoint. - // False for other bins, which do not generate as runs. + // False for other bins, which do not generate as runs, including a wildcard array then one + // bin (see BinRuns::single). bool binRunsFor(AstCoverBin* binp, AstNodeExpr* exprp, BinRuns& out) { if (isAutoBins(binp)) { if (!autoBinRuns(binp, exprp, out)) out.unsupported = true; return true; } + if (binp->isArray() && binp->isWildcard()) { + if (!exprp->dtypep()->skipRefp()->isIntegralOrPacked()) { + AstNodeExpr* const falsep = wildcardTypeError(binp, exprp); + VL_DO_DANGLING(pushDeletep(falsep), falsep); + out.unsupported = true; + } else { + out = wildcardBinRuns(binp, exprp, true); + if (out.single) { + binp->isArray(false); // Generates as one bin + return false; + } + } + return true; + } if (!binp->isArray() || binp->transp() || binp->isWildcard() || !exprp->dtypep()->skipRefp()->isIntegralOrPacked()) { return false; @@ -1257,9 +1426,10 @@ class FunctionalCoverageVisitor final : public VNVisitor { } // Emit a 'this->m_cp->addSingleNamer/addArrayNamer(...)' statement for one bin whose first - // runtime bin index is 'declared' + // runtime bin index is 'declared'; or with 'valueNames', those naming each bin of an array AstNodeStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int64_t count, uint32_t declared, - const std::vector& values = {}) { + const std::vector& values = {}, + const std::vector& valueNames = {}) { FileLine* const fl = binp->fileline(); CoverpointBins& bins = m_cpBins.at(cpVarp); const uint32_t normalCount @@ -1279,6 +1449,23 @@ class FunctionalCoverageVisitor final : public VNVisitor { // unit filename, per-word bin name). A no-op when --protect-ids is off. const bool prot = v3Global.opt.protectIds(); const bool single = count < 0; + if (!valueNames.empty()) { + // A bin of a wildcard array is named by its value (IEEE 1800-2023 19.5.1) + AstNodeStmt* stmtsp = nullptr; + for (const std::string& value : valueNames) { + const std::string name + = VIdProtect::protectWordsIf(binp->name(), prot) + "[" + value + "]"; + stmtsp = AstNode::addNext( + stmtsp, + itemCall(fl, cpVarp, VCMethod::COVERGROUP_ADD_SINGLE_NAMER, + {ctext(fl, binp->binsType().binSetEnum()), ctext(fl, quoted(name)), + ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))), + cnum(fl, static_cast(fl->lineno())), + cnum(fl, static_cast(fl->firstColumn()))}) + ->makeStmt()); + } + return stmtsp; + } std::vector args{ctext(fl, binp->binsType().binSetEnum())}; if (!single) args.push_back(cnum(fl, static_cast(count))); // value array bin args.push_back(ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot)))); @@ -1416,7 +1603,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { "(IEEE 1800-2023 6.20.7)"); continue; } - generateSizedElement(cpVarp, rangep, exprp, prefix + "_" + cvtToStr(element++)); + generateSizedElement(cpVarp, rangep, exprp, binp->isWildcard(), + prefix + "_" + cvtToStr(element++)); } const auto countRef = [&]() { return new AstVarRef{fl, countp, VAccess::READ}; }; AstConst* const zerop = new AstConst{fl, AstConst::DTyped{}, countp->dtypep()}; @@ -1438,7 +1626,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { m_constructorp->addStmtsp( itemCall(fl, cpVarp, VCMethod::COVERGROUP_SIZED_FINISH, {ctext(fl, binp->binsType().binSetEnum()), countValuep, positivep, - cnum(fl, binsLimit()), + cnum(fl, v3Global.opt.coverageMaxBins()), ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot))), ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))), cnum(fl, static_cast(fl->lineno())), @@ -1448,8 +1636,9 @@ class FunctionalCoverageVisitor final : public VNVisitor { // Emit 'sizedRange(lo, hi)' for the coverpoint values of an element of a sized array of // bins: resolved now if constant, else when constructed by clipping to the coverpoint's - // values. 'prefix' names its temporaries. - void generateSizedElement(AstVar* cpVarp, AstNode* rangep, AstNodeExpr* exprp, + // values. A 'wildcard' pattern's values give a range for each run of them. 'prefix' names + // its temporaries. + void generateSizedElement(AstVar* cpVarp, AstNode* rangep, AstNodeExpr* exprp, bool wildcard, const string& prefix) { FileLine* const fl = rangep->fileline(); const VCMethod method = exprp->isWide() ? VCMethod::COVERGROUP_SIZED_RANGE_W @@ -1475,14 +1664,21 @@ class FunctionalCoverageVisitor final : public VNVisitor { return; } CrossValueRange range{rangep, resolveWidth(rangep, exprp)}; - if (!resolveValue(rangep, exprp, true, false, range)) { + if (!resolveValue(rangep, exprp, true, wildcard, range)) { rangep->v3warn(E_UNSUPPORTED, "Unsupported: non-integral value in a coverage bin " "of an integral coverpoint."); } else if (!crossRangeEmpty(range)) { - m_constructorp->addStmtsp(itemCall(fl, cpVarp, method, - {newValueConst(fl, range.lo, exprp), - newValueConst(fl, range.hi, exprp)}) - ->makeStmt()); + std::vector> runs{{range.lo, range.hi}}; + if (range.wildcard) { // checkSizedArrays bounded the runs + runs.clear(); + crossRangeRuns(range, v3Global.opt.coverageMaxBins(), runs); + } + for (const std::pair& run : runs) { + m_constructorp->addStmtsp(itemCall(fl, cpVarp, method, + {newValueConst(fl, run.first, exprp), + newValueConst(fl, run.second, exprp)}) + ->makeStmt()); + } } return; } @@ -1718,7 +1914,8 @@ class FunctionalCoverageVisitor final : public VNVisitor { CoverpointBins& bins = m_cpBins.at(cpVarp); const uint32_t firstValue = bins.total; const uint32_t firstDeclared = static_cast(idx); - namerStmts.push_back(makeNamer(cpVarp, cbinp, plan.count, firstDeclared)); + namerStmts.push_back( + makeNamer(cpVarp, cbinp, plan.count, firstDeclared, {}, plan.values)); for (BinRun& run : plan.runs) { run.m_declared = static_cast(idx); bins.runs.push_back(std::move(run)); @@ -2502,6 +2699,59 @@ class FunctionalCoverageVisitor final : public VNVisitor { return states != 0; } + // The least value at least 'from' with the non-x bits of 'pattern', in unsigned order; false + // if none. A bit scan, which does not enumerate the pattern's x bits. + static bool nextPatternValue(const V3Number& pattern, const V3Number& from, V3Number& result) { + result.opAssign(from); + int carry = -1; // Lowest x bit above the bit scanned where 'from' has a 0 + for (int bit = from.width() - 1; bit >= 0; --bit) { + if (pattern.bitIsXZ(bit)) { + if (!from.bitIs1(bit)) carry = bit; + continue; + } + if (pattern.bitIs1(bit) == from.bitIs1(bit)) continue; + if (from.bitIs1(bit)) { // Only a greater prefix, an x bit above raised, can match + if (carry < 0) return false; + bit = carry; + } + // Then the least such value: the pattern's bits, with x bits of zero + result.setBit(bit, 1); + while (--bit >= 0) result.setBit(bit, pattern.bitIs1(bit)); + return true; + } + return true; + } + + // Append to 'runs' the maximal runs of consecutive values of 'range' that its pattern + // matches, in value order, until there are more than 'limit'. A match continues through + // the pattern's trailing x bits only, which the next value's carry leaves. + static void crossRangeRuns(const CrossValueRange& range, size_t limit, + std::vector>& runs) { + // Values order signed, which is the unsigned order of values with the sign bit flipped + const int sign = range.lo.width() - 1; + const auto flipped = [sign](V3Number value) { + if (!value.bitIsXZ(sign)) value.setBit(sign, !value.bitIs1(sign)); + return value; + }; + const V3Number pattern = flipped(range.pattern); + const V3Number hi = flipped(range.hi); + const V3Number one{&hi, hi.width(), 1}; + V3Number trailing{&hi, hi.width()}; + for (int bit = 0; bit <= sign && pattern.bitIsXZ(bit); ++bit) trailing.setBit(bit, 1); + V3Number from = flipped(range.lo); + V3Number first{&hi, hi.width()}; + V3Number last{&hi, hi.width()}; + V3Number less{&hi}; + while (runs.size() <= limit && nextPatternValue(pattern, from, first) + && less.opLt(hi, first).isEqZero()) { + last.opOr(first, trailing); + if (!less.opLt(hi, last).isEqZero()) last = hi; + runs.emplace_back(flipped(first), flipped(last)); + if (last.isCaseEq(hi)) break; + from.opAdd(last, one); + } + } + // True if no coverpoint value participates in a resolved value or range. A value with x // or z bits participates only as a wildcard pattern (IEEE 1800-2023 19.5.7). static bool crossRangeEmpty(const CrossValueRange& range) { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index ddfd691c7..b2442668a 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -343,11 +343,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { emitCoverageOption(nodep, nodep->typeOption(), nodep->optType(), nodep->valuep()); } void visit(AstCoverBin* nodep) override { - switch (nodep->binsType()) { - case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break; - case VCoverBinsType::BINS_ILLEGAL: putfs(nodep, "illegal_bins "); break; - default: putfs(nodep, "bins "); break; - } + putfs(nodep, std::string{nodep->isWildcard() ? "wildcard " : ""} + + nodep->binsType().verilogKwd() + " "); puts(nodep->name()); if (nodep->isArray()) { puts("["); diff --git a/src/verilog.y b/src/verilog.y index 3a7f96bac..2dfe570b2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7207,11 +7207,11 @@ bins_or_options: // ==IEEE: bins_or_options | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; GRAMMARP->coverBinArrayUnsized($3); BBCOVERIGN($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$3, *$3, $7, false, false, true, $9}; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$3, *$3, $7, false, false, true, $9}, $4); } | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$3, *$3, $7, true, false, true, $9}; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$3, *$3, $7, true, false, true, $9}, $4); } | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$3, *$3, $7, false, true, true, $9}; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$3, *$3, $7, false, true, true, $9}, $4); } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; GRAMMARP->coverBinArrayUnsized($4); BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE diff --git a/test_regress/t/t_covergroup_autobins_bad.out b/test_regress/t/t_covergroup_autobins_bad.out index 499144658..4567c77cc 100644 --- a/test_regress/t/t_covergroup_autobins_bad.out +++ b/test_regress/t/t_covergroup_autobins_bad.out @@ -238,4 +238,12 @@ : ... Suggest a larger --coverage-max-real-bins 257 | bins over_limit[] = {[0 : 1024]}; | ^~~~~~~~~~ +%Error: t/t_covergroup_autobins_bad.v:264:21: Non-constant expression in array bins value list; values must be constants (IEEE 1800-2023 19.5) + : ... note: In instance 't' + 264 | wildcard bins value[] = {size_var}; + | ^~~~~ +%Error: t/t_covergroup_autobins_bad.v:265:21: Non-constant expression in array bins range; range bounds must be constants (IEEE 1800-2023 19.5) + : ... note: In instance 't' + 265 | wildcard bins range[] = {[0 : size_var]}; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_autobins_bad.v b/test_regress/t/t_covergroup_autobins_bad.v index a31ee0bc0..69d8771d4 100644 --- a/test_regress/t/t_covergroup_autobins_bad.v +++ b/test_regress/t/t_covergroup_autobins_bad.v @@ -258,6 +258,16 @@ module t; } endgroup + // Error: non-constant values of wildcard arrays of bins, also of a crossed coverpoint + covergroup cg_wild_nonconst; + cp_a: coverpoint cp_expr { + wildcard bins value[] = {size_var}; + wildcard bins range[] = {[0 : size_var]}; + } + cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};} + xc: cross cp_a, cp_c; + endgroup + cg1 cg1_inst = new; cg2 cg2_inst = new; cg2b cg2b_inst = new; @@ -280,6 +290,7 @@ module t; cgx_binsof_many_values cgx_binsof_many_values_inst = new; cg_limit cg_limit_inst = new; cg_real cg_real_inst = new; + cg_wild_nonconst cg_wild_nonconst_inst = new; initial $finish; endmodule diff --git a/test_regress/t/t_covergroup_cross.out b/test_regress/t/t_covergroup_cross.out index 88ffe002d..804a39fee 100644 --- a/test_regress/t/t_covergroup_cross.out +++ b/test_regress/t/t_covergroup_cross.out @@ -193,11 +193,14 @@ cg_wide.wd.hi_x_read [cross]: 1 cg_wide.wd.hi_x_write [cross]: 1 cg_wide.wd.lo_x_read [cross]: 1 cg_wide.wd.lo_x_write [cross]: 1 -cg_wild_arr.cp_addr.wb: 4 +cg_wild_arr.cp_addr.wb[0]: 2 +cg_wild_arr.cp_addr.wb[1]: 2 cg_wild_arr.cp_cmd.read: 2 cg_wild_arr.cp_cmd.write: 2 -cg_wild_arr.wa.wb_x_read [cross]: 2 -cg_wild_arr.wa.wb_x_write [cross]: 2 +cg_wild_arr.wa.wb[0]_x_read [cross]: 1 +cg_wild_arr.wa.wb[0]_x_write [cross]: 1 +cg_wild_arr.wa.wb[1]_x_read [cross]: 1 +cg_wild_arr.wa.wb[1]_x_write [cross]: 1 cg_wild_solo.cp_addr.wb: 2 cg_wild_solo.cp_cmd.read: 1 cg_wild_solo.cp_cmd.write: 1 diff --git a/test_regress/t/t_covergroup_exclusions_unsup.out b/test_regress/t/t_covergroup_exclusions_unsup.out index 5b79d8a96..7cef59f7e 100644 --- a/test_regress/t/t_covergroup_exclusions_unsup.out +++ b/test_regress/t/t_covergroup_exclusions_unsup.out @@ -1,40 +1,54 @@ -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:14:20: Unsupported: non-integral value in a coverage bin of an integral coverpoint. +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:15:20: Unsupported: non-integral value in a coverage bin of an integral coverpoint. : ... note: In instance 't' - 14 | bins text = {TEXT}; + 15 | bins text = {TEXT}; | ^~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:15:28: Unsupported: non-integral value in a coverage bin of an integral coverpoint. +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:16:28: Unsupported: non-integral value in a coverage bin of an integral coverpoint. : ... note: In instance 't' - 15 | bins text_array[] = {TEXT}; + 16 | bins text_array[] = {TEXT}; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:16:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:18:36: Unsupported: non-integral value in a coverage bin of an integral coverpoint. : ... note: In instance 't' - 16 | bins text_sized[2] = {TEXT}; + 18 | wildcard bins text_wild[] = {TEXT}; + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:17:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. + : ... note: In instance 't' + 17 | bins text_sized[2] = {TEXT}; | ^~~~ -%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:28:12: Unsupported: 'bins' explicit array size of a real coverpoint (treated as '[]') +%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:30:12: Unsupported: 'bins' explicit array size of a real coverpoint (treated as '[]') : ... note: In instance 't' - 28 | bins sized[2] = {1.0, 2.0}; + 30 | bins sized[2] = {1.0, 2.0}; | ^~~~~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:25:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:27:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. : ... note: In instance 't' - 25 | bins real_bound[2] = {[lo : 5]}; + 27 | bins real_bound[2] = {[lo : 5]}; | ^ -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:34:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. +%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:37:21: Unsupported: sized wildcard array 'bins' of more than --coverage-max-bins of 1024 ranges of values; bin ignored + : ... note: In instance 't' + : ... Suggest a larger --coverage-max-bins + 37 | wildcard bins runs[2] = {16'b????_????_????_???1}; + | ^~~~ +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:43:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint. : ... note: In instance 't' - 34 | wildcard bins text = {TEXT}; + 43 | wildcard bins text = {TEXT}; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:40:25: Unsupported: non-integral value in a transition bin of a coverpoint with exclusions. +%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:49:25: Unsupported: non-integral value in a transition bin of a coverpoint with exclusions. : ... note: In instance 't' - 40 | bins text = (1 => TEXT); + 49 | bins text = (1 => TEXT); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:53:59: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound. +%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:62:59: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound. : ... note: In instance 't' - 53 | static_cross: cross cp_real, cp_plain{bins selected = binsof (cp_real) intersect {1};} + 62 | static_cross: cross cp_real, cp_plain{bins selected = binsof (cp_real) intersect {1};} | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:54:62: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound. +%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:63:62: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound. : ... note: In instance 't' - 54 | dynamic_cross: cross cp_real, cp_dynamic{bins selected = binsof (cp_real) intersect {1};} + 63 | dynamic_cross: cross cp_real, cp_dynamic{bins selected = binsof (cp_real) intersect {1};} | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:69:29: Unsupported: sized wildcard array 'illegal_bins' of more than --coverage-max-bins of 1024 ranges of values; treated as one bin + : ... note: In instance 't' + : ... Suggest a larger --coverage-max-bins + 69 | wildcard illegal_bins runs[2] = {16'b????_????_????_???1}; + | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_exclusions_unsup.v b/test_regress/t/t_covergroup_exclusions_unsup.v index 30e9a553d..92fad3dba 100644 --- a/test_regress/t/t_covergroup_exclusions_unsup.v +++ b/test_regress/t/t_covergroup_exclusions_unsup.v @@ -6,6 +6,7 @@ module t; bit [3:0] value; + bit [15:0] wide_value; real real_value; localparam string TEXT = "a"; @@ -14,6 +15,7 @@ module t; bins text = {TEXT}; bins text_array[] = {TEXT}; bins text_sized[2] = {TEXT}; + wildcard bins text_wild[] = {TEXT}; ignore_bins ignored = {0}; } endgroup @@ -29,6 +31,13 @@ module t; } endgroup + // A sized wildcard array whose pattern holds more ranges of values than --coverage-max-bins + covergroup cg_wild_runs; + cp: coverpoint wide_value { + wildcard bins runs[2] = {16'b????_????_????_???1}; + } + endgroup + covergroup cg_wild; cp: coverpoint value { wildcard bins text = {TEXT}; @@ -54,11 +63,20 @@ module t; dynamic_cross: cross cp_real, cp_dynamic{bins selected = binsof (cp_real) intersect {1};} endgroup + // A sized wildcard illegal array of as many ranges of values, treated as one bin + covergroup cg_wild_runs_illegal; + cp: coverpoint wide_value { + wildcard illegal_bins runs[2] = {16'b????_????_????_???1}; + } + endgroup + cg_values values_cov = new; cg_sized sized_cov = new(1.0); + cg_wild_runs wild_runs_cov = new; cg_wild wild_cov = new; cg_transition transition_cov = new; cg_cross cross_cov = new; + cg_wild_runs_illegal wild_runs_illegal_cov = new; initial $finish; endmodule diff --git a/test_regress/t/t_covergroup_max_bins_unsup.out b/test_regress/t/t_covergroup_max_bins_unsup.out index 59b05a11d..a3fdfc59a 100644 --- a/test_regress/t/t_covergroup_max_bins_unsup.out +++ b/test_regress/t/t_covergroup_max_bins_unsup.out @@ -5,20 +5,30 @@ | ^~~~~~~~~~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Error: t/t_covergroup_max_bins_unsup.v:21:12: Automatic bins array size of 4294967296 exceeds limit of 4294967295 +%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:21:21: Unsupported: wildcard array 'bins' of more than --coverage-max-bins of 4294967295 values; bin ignored + : ... note: In instance 't' + : ... Suggest a larger --coverage-max-bins + 21 | wildcard bins over_limit[] = {32'h????_????}; + | ^~~~~~~~~~ +%Error: t/t_covergroup_max_bins_unsup.v:25:12: Automatic bins array size of 4294967296 exceeds limit of 4294967295 : ... note: In instance 't' : ... Suggest a larger --coverage-max-bins - 21 | bins auto[33'd4294967296]; + 25 | bins auto[33'd4294967296]; | ^~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_covergroup_max_bins_unsup.v:24:15: Unsupported: coverpoint with more than 4294967295 bins +%Error-UNSUPPORTED: t/t_covergroup_max_bins_unsup.v:28:15: Unsupported: coverpoint with more than 4294967295 bins : ... note: In instance 't' - 24 | cp_total: coverpoint data { + 28 | cp_total: coverpoint data { | ^~~~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:30:12: Unsupported: array 'bins' of a real coverpoint covering more than 2 values; bin ignored. +%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:34:12: Unsupported: array 'bins' of a real coverpoint covering more than 2 values; bin ignored. : ... note: In instance 't' : ... Suggest a larger --coverage-max-real-bins - 30 | bins over_limit[] = {[1 : 3]}; + 34 | bins over_limit[] = {[1 : 3]}; | ^~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:38:28: Unsupported: wildcard array 'ignore_bins' of more than --coverage-max-bins of 4294967295 values; treated as one bin + : ... note: In instance 't' + : ... Suggest a larger --coverage-max-bins + 38 | wildcard ignore_bins over_limit[] = {32'h????_????}; + | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_max_bins_unsup.v b/test_regress/t/t_covergroup_max_bins_unsup.v index d35fb7998..38ae48119 100644 --- a/test_regress/t/t_covergroup_max_bins_unsup.v +++ b/test_regress/t/t_covergroup_max_bins_unsup.v @@ -16,6 +16,10 @@ module t; cp_array_over: coverpoint data { bins over_limit[] = {[0 : $]}; } + // Warning (COVERIGN): a wildcard array of more values than the limit + cp_wild_over: coverpoint data { + wildcard bins over_limit[] = {32'h????_????}; + } // Error: automatic bins over the limit cp_auto_over: coverpoint data { bins auto[33'd4294967296]; @@ -29,6 +33,10 @@ module t; bins at_limit[] = {[1 : 2]}; bins over_limit[] = {[1 : 3]}; // Warning (COVERIGN): 3 values } + // Warning (COVERIGN): a wildcard ignore array of more values than the limit, as one bin + cp_wild_ignore: coverpoint data { + wildcard ignore_bins over_limit[] = {32'h????_????}; + } endgroup cg cg_inst = new; diff --git a/test_regress/t/t_covergroup_wild_real_bad.out b/test_regress/t/t_covergroup_wild_real_bad.out index 560aee807..6ac5ac79e 100644 --- a/test_regress/t/t_covergroup_wild_real_bad.out +++ b/test_regress/t/t_covergroup_wild_real_bad.out @@ -20,4 +20,11 @@ t/t_covergroup_wild_real_bad.v:18:21: ... Location of wildcard bin 18 | wildcard bins open = {[1 : $]}; | ^~~~ +%Error: t/t_covergroup_wild_real_bad.v:20:15: Cannot use a wildcard bin on a coverpoint of type 'real' (IEEE 1800-2023 19.5.4). + : ... note: In instance 't' + 20 | cp_array: coverpoint value { + | ^~~~~~~~~~ + t/t_covergroup_wild_real_bad.v:21:21: ... Location of wildcard bin + 21 | wildcard bins array[] = {1}; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_wild_real_bad.v b/test_regress/t/t_covergroup_wild_real_bad.v index ff8e86cb6..dc0c44ab0 100644 --- a/test_regress/t/t_covergroup_wild_real_bad.v +++ b/test_regress/t/t_covergroup_wild_real_bad.v @@ -17,6 +17,9 @@ module t; cp_open: coverpoint value { wildcard bins open = {[1 : $]}; } + cp_array: coverpoint value { + wildcard bins array[] = {1}; + } endgroup cg cov = new; diff --git a/test_regress/t/t_covergroup_wildcard_bins.out b/test_regress/t/t_covergroup_wildcard_bins.out index 6cedb321a..79d9da92a 100644 --- a/test_regress/t/t_covergroup_wildcard_bins.out +++ b/test_regress/t/t_covergroup_wildcard_bins.out @@ -3,3 +3,144 @@ cg.data.low: 2 cg.data.mid_range: 1 cg.data.pattern: 2 cg.data.wc_point: 2 +cg_arrays.dups.b[11]: 4 +cg_arrays.dups.b[12]: 4 +cg_arrays.dups.b[13]: 4 +cg_arrays.dups.b[14]: 4 +cg_arrays.dups.b[15]: 4 +cg_arrays.excl.b[2]: 8 +cg_arrays.excl.b[3]: 8 +cg_arrays.excl.i[0] [ignore]: 12 +cg_arrays.excl.i[1] [ignore]: 12 +cg_arrays.excl.l[7] [illegal]: 0 +cg_arrays.excl.others [default]: 24 +cg_arrays.gaps.b[12]: 4 +cg_arrays.gaps.b[13]: 4 +cg_arrays.gaps.b[8]: 4 +cg_arrays.gaps.b[9]: 4 +cg_arrays.gated.b[12]: 1 +cg_arrays.gated.b[14]: 1 +cg_arrays.ieee.g12_15_array[12]: 4 +cg_arrays.ieee.g12_15_array[13]: 4 +cg_arrays.ieee.g12_15_array[14]: 4 +cg_arrays.ieee.g12_15_array[15]: 4 +cg_arrays.mixed.b[0]: 4 +cg_arrays.mixed.b[1]: 4 +cg_arrays.mixed.b[5]: 4 +cg_arrays.mixed.b[8]: 4 +cg_arrays.mixed.b[9]: 4 +cg_arrays.nonzero.b[2]: 16 +cg_arrays.nonzero.b[3]: 16 +cg_arrays.sgn.cast[-1]: 4 +cg_arrays.sgn.cast[-2]: 4 +cg_arrays.sgn.cast[-3]: 4 +cg_arrays.sgn.cast[-4]: 4 +cg_arrays.sgn.cast[-5]: 4 +cg_arrays.sgn.cast[-6]: 4 +cg_arrays.sgn.cast[-7]: 4 +cg_arrays.sgn.cast[-8]: 4 +cg_arrays.sgn.ext[-1]: 4 +cg_arrays.sgn.ext[-2]: 4 +cg_arrays.sgn.ext[-3]: 4 +cg_arrays.sgn.ext[-4]: 4 +cg_arrays.sgn2.b[-1]: 16 +cg_arrays.sgn2.b[1]: 16 +cg_arrays.split.b[1]: 12 +cg_arrays.split.b[5]: 8 +cg_arrays.split.i[0] [ignore]: 12 +cg_arrays.split.i[4] [ignore]: 8 +cg_arrays.wide.keep[12]: 4 +cg_arrays.wide.keep[13]: 4 +cg_arrays.wide.keep[14]: 4 +cg_arrays.wide.keep[15]: 4 +cg_arrays.wide.keep[1]: 4 +cg_arrays.wide100.b[16]: 4 +cg_arrays.wide100.b[17]: 4 +cg_arrays.wide100.b[18]: 4 +cg_arrays.wide100.b[19]: 4 +cg_arrays.wide100.b[20]: 4 +cg_arrays.wide100.b[21]: 4 +cg_arrays.wide100.b[22]: 4 +cg_arrays.wide100.b[23]: 4 +cg_arrays.wide100.b[24]: 4 +cg_arrays.wide100.b[25]: 4 +cg_arrays.wide100.b[26]: 4 +cg_arrays.wide100.b[27]: 4 +cg_arrays.wide100.b[28]: 4 +cg_arrays.wide100.b[29]: 4 +cg_arrays.wide100.b[30]: 4 +cg_arrays.wide100.b[31]: 4 +cg_arrays.wide100.f[24]: 4 +cg_arrays.wide100.f[25]: 4 +cg_arrays.wide100.f[28]: 4 +cg_arrays.wide100.f[29]: 4 +cg_arrays.ww.b[2]_x_b[16] [cross]: 1 +cg_arrays.ww.b[2]_x_b[17] [cross]: 1 +cg_arrays.ww.b[2]_x_b[18] [cross]: 1 +cg_arrays.ww.b[2]_x_b[19] [cross]: 1 +cg_arrays.ww.b[2]_x_b[20] [cross]: 1 +cg_arrays.ww.b[2]_x_b[21] [cross]: 1 +cg_arrays.ww.b[2]_x_b[22] [cross]: 1 +cg_arrays.ww.b[2]_x_b[23] [cross]: 1 +cg_arrays.ww.b[2]_x_b[24] [cross]: 1 +cg_arrays.ww.b[2]_x_b[25] [cross]: 1 +cg_arrays.ww.b[2]_x_b[26] [cross]: 1 +cg_arrays.ww.b[2]_x_b[27] [cross]: 1 +cg_arrays.ww.b[2]_x_b[28] [cross]: 1 +cg_arrays.ww.b[2]_x_b[29] [cross]: 1 +cg_arrays.ww.b[2]_x_b[30] [cross]: 1 +cg_arrays.ww.b[2]_x_b[31] [cross]: 1 +cg_arrays.ww.b[2]_x_f[24] [cross]: 1 +cg_arrays.ww.b[2]_x_f[25] [cross]: 1 +cg_arrays.ww.b[2]_x_f[28] [cross]: 1 +cg_arrays.ww.b[2]_x_f[29] [cross]: 1 +cg_arrays.ww.b[3]_x_b[16] [cross]: 1 +cg_arrays.ww.b[3]_x_b[17] [cross]: 1 +cg_arrays.ww.b[3]_x_b[18] [cross]: 1 +cg_arrays.ww.b[3]_x_b[19] [cross]: 1 +cg_arrays.ww.b[3]_x_b[20] [cross]: 1 +cg_arrays.ww.b[3]_x_b[21] [cross]: 1 +cg_arrays.ww.b[3]_x_b[22] [cross]: 1 +cg_arrays.ww.b[3]_x_b[23] [cross]: 1 +cg_arrays.ww.b[3]_x_b[24] [cross]: 1 +cg_arrays.ww.b[3]_x_b[25] [cross]: 1 +cg_arrays.ww.b[3]_x_b[26] [cross]: 1 +cg_arrays.ww.b[3]_x_b[27] [cross]: 1 +cg_arrays.ww.b[3]_x_b[28] [cross]: 1 +cg_arrays.ww.b[3]_x_b[29] [cross]: 1 +cg_arrays.ww.b[3]_x_b[30] [cross]: 1 +cg_arrays.ww.b[3]_x_b[31] [cross]: 1 +cg_arrays.ww.b[3]_x_f[24] [cross]: 1 +cg_arrays.ww.b[3]_x_f[25] [cross]: 1 +cg_arrays.ww.b[3]_x_f[28] [cross]: 1 +cg_arrays.ww.b[3]_x_f[29] [cross]: 1 +cg_arrays.xx.b[2]_x_b[12] [cross]: 1 +cg_arrays.xx.b[2]_x_b[13] [cross]: 1 +cg_arrays.xx.b[3]_x_b[12] [cross]: 1 +cg_arrays.xx.b[3]_x_b[13] [cross]: 1 +cg_arrays.xx.sel [cross]: 4 +cg_s64.neg.b[-1]: 1 +cg_s64.neg.b[-3]: 1 +cg_s64.nx.b[-1]_x_b [cross]: 1 +cg_s64.nx.b[-3]_x_b [cross]: 1 +cg_s64.three.b: 18 +cg_s64.top.b[-1152921504606846978]: 1 +cg_s64.top.b[-2305843009213693954]: 1 +cg_s64.top.b[-2]: 1 +cg_s64.top.b[-3458764513820540930]: 1 +cg_s64.top.b[-4611686018427387906]: 1 +cg_s64.top.b[-5764607523034234882]: 1 +cg_s64.top.b[-6917529027641081858]: 1 +cg_s64.top.b[-8070450532247928834]: 1 +cg_s64.top.b[1152921504606846974]: 1 +cg_s64.top.b[2305843009213693950]: 1 +cg_s64.top.b[3458764513820540926]: 1 +cg_s64.top.b[4611686018427387902]: 1 +cg_s64.top.b[5764607523034234878]: 1 +cg_s64.top.b[6917529027641081854]: 1 +cg_s64.top.b[8070450532247928830]: 1 +cg_s64.top.b[9223372036854775806]: 1 +cg_sized.d.b[0]: 1 +cg_sized.d.b[1]: 1 +cg_sized.s.b[0]: 1 +cg_sized.s.b[1]: 1 diff --git a/test_regress/t/t_covergroup_wildcard_bins.v b/test_regress/t/t_covergroup_wildcard_bins.v index d1181557c..8474ea93e 100644 --- a/test_regress/t/t_covergroup_wildcard_bins.v +++ b/test_regress/t/t_covergroup_wildcard_bins.v @@ -13,6 +13,13 @@ module t; bit [7:0] data; + bit [3:0] v; + bit [1:0] v2; + bit signed [3:0] sv; + bit signed [1:0] sv2; + bit [2:0] u; + bit [99:0] w; + longint l; covergroup cg; coverpoint data { @@ -33,6 +40,102 @@ module t; } endgroup + // Wildcard arrays: a bin for each value a pattern matches, named by the value (IEEE 1800-2023 + // 19.5.4, 19.5.1) + covergroup cg_arrays; + // The example of IEEE 1800-2023 19.5.4: 12..15 + ieee: coverpoint v { + wildcard bins g12_15_array[] = {4'b11??}; + } + // Values that are not consecutive: 8, 9, 12, 13 + gaps: coverpoint v { + wildcard bins b[] = {4'b1?0?}; + } + // A value two patterns match has one bin: 11..15 + dups: coverpoint v { + wildcard bins b[] = {4'b11??, 4'b1?11}; + } + // Patterns, values, and ranges: 0, 1, 5, 8, 9 + mixed: coverpoint v { + wildcard bins b[] = {4'b000?, 5, [8 : 9]}; + } + // Only values of the coverpoint type (IEEE 1800-2023 19.5.7): 1 and 12..15, and none + wide: coverpoint v { + wildcard bins keep[] = {8'b0000_11??, 8'b????_0001}; + wildcard bins none[] = {8'b1???_0000}; + } + // Signed values: -1 and 1; -4..-1 of a signed pattern, none of a wider unsigned one, and + // -8..-1 of one of the same width, cast to the coverpoint type + sgn2: coverpoint sv2 { + wildcard bins b[] = {2'sb?1}; + } + sgn: coverpoint sv { + wildcard bins ext[] = {8'sb1111_11??}; + wildcard bins none[] = {8'b1111_11??}; + wildcard bins cast[] = {4'b1???}; + } + // Excluded values leave the array, 0 and 1, and a default bin holds the others: 4..6 + excl: coverpoint u { + wildcard bins b[] = {3'b0??}; + wildcard ignore_bins i[] = {3'b00?}; + wildcard illegal_bins l[] = {3'b111}; + bins others = default; + } + // Excluded values also leave values that are not consecutive: 1 and 5 + split: coverpoint u { + wildcard bins b[] = {3'b?0?}; + wildcard ignore_bins i[] = {3'b?00}; + } + gated: coverpoint v { + wildcard bins b[] = {4'b11?0} iff (v2 == 3); + } + nonzero: coverpoint v2 { + wildcard bins b[] = {2'b1?}; + } + // Crosses of wildcard arrays, one of whose bins are selected by value + xx: cross nonzero, gaps{ + bins sel = binsof (gaps.b) intersect {[8 : 9]}; + } + // Values beyond 64 bits: 16..31, and 24, 25, 28, 29 + wide100: coverpoint w { + wildcard bins b[] = {100'h1?}; + wildcard bins f[] = {100'b1_1?0?}; + } + ww: cross nonzero, wide100; + endgroup + + // Sized wildcard arrays distribute the values in order, and retain duplicates (IEEE + // 1800-2023 19.5.1) + covergroup cg_sized; + // <8,9>, <12,13> + s: coverpoint v { + wildcard bins b[2] = {4'b1?0?}; + } + // <12,13,14>, <15,11,15> + d: coverpoint v { + wildcard bins b[2] = {4'b11??, 4'b1?11}; + } + endgroup + + // Signed 64-bit values: a crossed array of negative ones, and those of a pattern with an x + // sign bit, in value order + covergroup cg_s64; + neg: coverpoint l { + wildcard bins b[] = {-1, -3}; + } + top: coverpoint l { + wildcard bins b[] = {64'sh?fff_ffff_ffff_fffe}; + } + three: coverpoint v2 { + bins b = {3}; + } + nx: cross neg, three; + endgroup + + cg_arrays arrays_inst = new; + cg_sized sized_inst = new; + cg_s64 s64_inst = new; + initial begin cg cg_inst; @@ -74,6 +177,44 @@ module t; cg_inst.sample(); `checkr(cg_inst.get_inst_coverage(), 100.0); + for (int a = 0; a < 4; ++a) begin + for (int i = 0; i < 16; ++i) begin + v = 4'(i); + sv = 4'(i); + v2 = 2'(a); + sv2 = 2'(a); + u = 3'(i % 7); + w = 100'(i + 16); + arrays_inst.sample(); + end + // With v2 == 0: all but sgn2, gated, nonzero, xx, and ww of the 14 items with bins + if (a == 0) `checkr(arrays_inst.get_inst_coverage(), 100.0 * 9 / 14); + end + `checkr(arrays_inst.get_inst_coverage(), 100.0); + + v = 8; + sized_inst.sample(); + `checkr(sized_inst.get_inst_coverage(), 100.0 * (1.0 / 2 + 0.0) / 2); + v = 11; + sized_inst.sample(); + `checkr(sized_inst.get_inst_coverage(), 100.0 * (1.0 / 2 + 1.0 / 2) / 2); + v = 13; + sized_inst.sample(); + `checkr(sized_inst.get_inst_coverage(), 100.0); + + v2 = 3; + for (int i = 0; i < 16; ++i) begin + l = {4'(i), 60'hfff_ffff_ffff_fffe}; + s64_inst.sample(); + end + // All of top and three, none of neg and nx + `checkr(s64_inst.get_inst_coverage(), 100.0 * 2 / 4); + l = -1; + s64_inst.sample(); + l = -3; + s64_inst.sample(); + `checkr(s64_inst.get_inst_coverage(), 100.0); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_wildcard_max_bins.out b/test_regress/t/t_covergroup_wildcard_max_bins.out new file mode 100644 index 000000000..8ccb297c2 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins.out @@ -0,0 +1,8 @@ +cg.ign.all: 8 +cg.ign.odd [ignore]: 8 +cg.ign_sized.all: 8 +cg.ign_sized.odd [ignore]: 8 +cg.ill.all: 16 +cg.ill.high [illegal]: 0 +cg.ill_sized.all: 16 +cg.ill_sized.odd [illegal]: 0 diff --git a/test_regress/t/t_covergroup_wildcard_max_bins.py b/test_regress/t/t_covergroup_wildcard_max_bins.py new file mode 100644 index 000000000..ca7b15561 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test, verilator_flags2=['--coverage-max-bins 4', '-Wno-COVERIGN']) diff --git a/test_regress/t/t_covergroup_wildcard_max_bins.v b/test_regress/t/t_covergroup_wildcard_max_bins.v new file mode 100644 index 000000000..f02652e91 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins.v @@ -0,0 +1,62 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Wildcard arrays of bins of more values, or ranges of values, than --coverage-max-bins 4. An +// ignore or illegal array still excludes or checks all its values, as one bin, and an ignored +// array leaves its coverpoint without bins, rather than automatic ones (IEEE 1800-2023 19.11.1). + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + bit [3:0] v; + bit [3:0] x; + int illegal; + + covergroup cg; + // The odd values are excluded, so 'all' holds the even ones + ign: coverpoint v { + bins all = {[0 : 15]}; + wildcard ignore_bins odd[] = {4'b???1}; + } + ign_sized: coverpoint v { + bins all = {[0 : 15]}; + wildcard ignore_bins odd[2] = {4'b???1}; + } + // Values 8..15, and the odd values, are illegal + ill: coverpoint x { + bins all = {[0 : 15]}; + wildcard illegal_bins high[] = {4'b1???}; + } + ill_sized: coverpoint x { + bins all = {[0 : 15]}; + wildcard illegal_bins odd[2] = {4'b???1}; + } + only: coverpoint v { + wildcard bins odd[2] = {4'b???1}; + } + endgroup + + cg cg_inst = new; + + initial begin + for (int i = 0; i < 16; ++i) begin + v = 4'(i); + x = 4'(i % 4 * 2); + cg_inst.sample(); + end + // All of the coverpoints with bins, which 'only' is not + `checkr(cg_inst.get_inst_coverage(), 100.0); + if ($value$plusargs("illegal=%d", illegal)) begin + x = 4'(illegal); + cg_inst.sample(); + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out new file mode 100644 index 000000000..894db71b2 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out @@ -0,0 +1,3 @@ +[0] %Error: t_covergroup_wildcard_max_bins.v:34: Assertion failed in top.t.cg.sample: Illegal bin 'high' hit in coverpoint 'ill' +%Error: t/t_covergroup_wildcard_max_bins.v:34: Verilog $stop +Aborting... diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal.py b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.py new file mode 100644 index 000000000..034a0ee3b --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.top_filename = 't/t_covergroup_wildcard_max_bins.v' + +test.compile(verilator_flags2=['--coverage', '--coverage-max-bins 4', '-Wno-COVERIGN']) + +# An illegal value of the array of more values than the limit +test.execute(fails=True, + check_finished=False, + all_run_flags=['+illegal=8'], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out new file mode 100644 index 000000000..d9d6a45e3 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out @@ -0,0 +1,3 @@ +[0] %Error: t_covergroup_wildcard_max_bins.v:38: Assertion failed in top.t.cg.sample: Illegal bin 'odd' hit in coverpoint 'ill_sized' +%Error: t/t_covergroup_wildcard_max_bins.v:38: Verilog $stop +Aborting... diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.py b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.py new file mode 100644 index 000000000..49b188929 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.top_filename = 't/t_covergroup_wildcard_max_bins.v' + +test.compile(verilator_flags2=['--coverage', '--coverage-max-bins 4', '-Wno-COVERIGN']) + +# An illegal value of the sized array of more ranges of values than the limit +test.execute(fails=True, + check_finished=False, + all_run_flags=['+illegal=3'], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index beceb0822..3371bbab6 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -801,6 +801,7 @@ module Vt_debug_emitv_t; bins high = {['sh4:'sh6]}; bins multi = {'sh0, 'sh1, 'sh2}; bins sized['sh2] = {['sh0:'sh5], 'sh7}; + wildcard bins wild[] = {3'b1z}; bins dflt = default; ignore_bins ign = {'sh7}; illegal_bins ill = {'sh5}; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index b81f23f0b..d46bf4129 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -400,6 +400,7 @@ module t (/*AUTOARG*/ bins high = {[4:6]}; bins multi = {0, 1, 2}; // multiple values in one bins (exercises EmitV range loop) bins sized[2] = {[0:5], 7}; // sized array of bins (exercises EmitV array size) + wildcard bins wild[] = {3'b01?}; // wildcard array of bins (exercises EmitV wildcard) bins dflt = default; ignore_bins ign = {7}; illegal_bins ill = {5}; diff --git a/test_regress/t/t_vlcov_covergroup.annotate.out b/test_regress/t/t_vlcov_covergroup.annotate.out index 28b8f84c7..2af214f17 100644 --- a/test_regress/t/t_vlcov_covergroup.annotate.out +++ b/test_regress/t/t_vlcov_covergroup.annotate.out @@ -540,16 +540,21 @@ // Crossed coverpoint with a wildcard array bin (wildcard bins wb[] = {2'b0?}): the '0?' // wildcard expands to addr values 0 and 1, each its own array element. covergroup cg_wild_arr; -%000004 cp_addr: coverpoint addr {wildcard bins wb[] = {2'b0?};} --000004 point: type=covergroup comment= hier=cg_wild_arr.cp_addr.wb +%000002 cp_addr: coverpoint addr {wildcard bins wb[] = {2'b0?};} +-000002 point: type=covergroup comment= hier=cg_wild_arr.cp_addr.wb[0] +-000002 point: type=covergroup comment= hier=cg_wild_arr.cp_addr.wb[1] %000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};} -000002 point: type=covergroup comment= hier=cg_wild_arr.cp_cmd.read -000002 point: type=covergroup comment= hier=cg_wild_arr.cp_cmd.write -%000002 wa: cross cp_addr, cp_cmd; --000002 point: type=covergroup comment= hier=cg_wild_arr.wa.wb_x_read - // cross: [wb, read] --000002 point: type=covergroup comment= hier=cg_wild_arr.wa.wb_x_write - // cross: [wb, write] +%000001 wa: cross cp_addr, cp_cmd; +-000001 point: type=covergroup comment= hier=cg_wild_arr.wa.wb[0]_x_read + // cross: [wb[0], read] +-000001 point: type=covergroup comment= hier=cg_wild_arr.wa.wb[0]_x_write + // cross: [wb[0], write] +-000001 point: type=covergroup comment= hier=cg_wild_arr.wa.wb[1]_x_read + // cross: [wb[1], read] +-000001 point: type=covergroup comment= hier=cg_wild_arr.wa.wb[1]_x_write + // cross: [wb[1], write] endgroup // Crossed coverpoint with a non-array wildcard bin (wildcard bins wb = {2'b0?}): single