Support wildcard bins (#8516)

This commit is contained in:
Marco Bartoli
2026-09-27 21:06:09 -04:00
committed by GitHub
parent 2c441d853c
commit 4ab229e659
25 changed files with 849 additions and 86 deletions
+6
View File
@@ -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) {
+294 -44
View File
@@ -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<AstCoverpoint*>
@@ -266,6 +268,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
std::vector<BinRun> 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<std::string> 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<uint32_t>(count);
return count < 1 || count > v3Global.opt.coverageMaxBins() ? 0
: static_cast<uint32_t>(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<std::pair<V3Number, V3Number>> 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<std::pair<uint64_t, uint64_t>> 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<std::vector<std::pair<uint64_t, uint64_t>>>& 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<std::pair<V3Number, V3Number>> 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<std::pair<V3Number, V3Number>> found;
crossRangeRuns(range, v3Global.opt.coverageMaxBins(), found);
for (const std::pair<V3Number, V3Number>& 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<std::pair<V3Number, V3Number>> merged;
for (const std::pair<V3Number, V3Number>& 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<V3Number, V3Number>& 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<V3Number, V3Number>& 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<uint32_t>(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<AstNodeExpr*>& values = {}) {
const std::vector<AstNodeExpr*>& values = {},
const std::vector<std::string>& 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<uint32_t>(fl->lineno())),
cnum(fl, static_cast<uint32_t>(fl->firstColumn()))})
->makeStmt());
}
return stmtsp;
}
std::vector<AstNodeExpr*> args{ctext(fl, binp->binsType().binSetEnum())};
if (!single) args.push_back(cnum(fl, static_cast<uint32_t>(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<uint32_t>(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<std::pair<V3Number, V3Number>> runs{{range.lo, range.hi}};
if (range.wildcard) { // checkSizedArrays bounded the runs
runs.clear();
crossRangeRuns(range, v3Global.opt.coverageMaxBins(), runs);
}
for (const std::pair<V3Number, V3Number>& 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<uint32_t>(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<uint32_t>(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<std::pair<V3Number, V3Number>>& 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) {
+2 -5
View File
@@ -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("[");
+3 -3
View File
@@ -7207,11 +7207,11 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; GRAMMARP->coverBinArrayUnsized($3); BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true, $9}; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, $7, false, false, true, $9}, $4); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true, $9}; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, $7, true, false, true, $9}, $4); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ GRAMMARP->coverBinArrayUnsized($4); $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true, $9}; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>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($<fl>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
@@ -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
@@ -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
+6 -3
View File
@@ -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
@@ -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
@@ -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
+16 -6
View File
@@ -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
@@ -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;
@@ -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
@@ -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;
@@ -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
+141
View File
@@ -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
@@ -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
@@ -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'])
@@ -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
@@ -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...
@@ -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()
@@ -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...
@@ -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()
+1
View File
@@ -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};
+1
View File
@@ -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};
+12 -7
View File
@@ -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