Support coverpoint bin 'with' filters (#8518)

This commit is contained in:
Marco Bartoli
2026-09-28 18:29:54 -04:00
committed by GitHub
parent a949674a6b
commit bde1b98ee0
48 changed files with 2366 additions and 293 deletions
+276 -12
View File
@@ -26,6 +26,7 @@
#include "verilated.h"
#include <map>
#include <set>
#include <tuple>
// This file is compiled whenever covergroups are used, with or without
@@ -121,7 +122,21 @@ struct VlCoverpoint::ValueData final {
std::vector<SizedElement> m_elements; // In declaration order
std::vector<SizedFast> m_fast; // The elements, if none is wider than 64 bits
uint64_t m_fastPerBin = 0; // B, with m_fast
bool m_sorted = false; // Elements disjoint and in value order
bool m_unit = false; // B is one, so positions, which are bins, are below 2^33
};
// The bins of a 'with' filter being built (IEEE 1800-2023 19.5.1.1)
struct With final {
VlCovBinGrouping m_grouping = VlCovBinGrouping::Single; // How the bins hold values
uint32_t m_limit = 0; // Most bins, or runs of values kept
size_t m_next = 0; // Candidate runs withNext() gave, of m_sizedElements
bool m_candidates = false; // More than CANDIDATE_LIMIT candidates: no bins
bool m_full = false; // Too many values kept: no bins
std::vector<Range> m_kept; // Runs of values kept, in order
Value m_values; // Values of m_kept, a position, for Values grouping
};
// Candidates each bin's filter may evaluate, when the covergroup is constructed
static constexpr QData CANDIDATE_LIMIT = 1ULL << 32;
// MEMBERS
const uint32_t m_bits; // Width of the coverpoint's effective integral type
@@ -134,6 +149,7 @@ struct VlCoverpoint::ValueData final {
std::vector<uint32_t> m_reported; // Declared bins that have values, in declaration order
std::vector<Sized> m_sized; // Sized arrays, in sizedFinish() order
std::vector<SizedElement> m_sizedElements; // sizedRange() elements of the next array
With m_with; // The bins of a 'with' filter being built
ValueData(uint32_t bits, bool isSigned, uint32_t bins)
: m_bits{bits}
@@ -215,6 +231,36 @@ struct VlCoverpoint::ValueData final {
uint64_t orderValue(uint64_t value) const {
return m_isSigned ? value ^ VL_BIT_Q(m_bits - 1) : value;
}
// Add the number of values lo..hi, lo not after hi, to the position 'total'
void countValues(const Value& lo, const Value& hi, Value& total) const {
increase(total, distance(view(lo), view(hi)));
increase(total, toPosition(1));
}
// The decimal value of a bin of a 'with' filter's array of a bin per value, which names it
std::string valueName(uint32_t bin) const {
// Arrays hold consecutive bins, so the last array starting at or before the bin holds it
const Sized& sized = *std::prev(std::upper_bound(
m_sized.begin(), m_sized.end(), bin,
[](uint32_t index, const Sized& array) { return index < array.m_first; }));
const uint32_t offset = bin - sized.m_first;
// Its elements are runs of its bins' values, each at the position of its first's bin
const SizedElement& element
= *std::prev(std::upper_bound(sized.m_elements.begin(), sized.m_elements.end(), offset,
[](uint32_t index, const SizedElement& run) {
return index < number(run.m_position);
}));
Value value = element.m_lo;
add(value, toPosition(offset - number(element.m_position)));
if (m_bits <= VL_QUADSIZE) {
const uint64_t word = number(value);
return m_isSigned
? std::to_string(static_cast<int64_t>(VL_EXTENDS_QQ(64, m_bits, word)))
: std::to_string(word);
}
return VL_SFORMATF_N_NX("%0d", 1,
m_isSigned ? VL_VFORMATATTR_SIGNED : VL_VFORMATATTR_UNSIGNED,
static_cast<int>(m_bits), value.data());
}
bool contains(const Range& range, WDataInP value) const {
if (less(value, range.m_lo) || less(range.m_hi, value)) return false;
if (!range.m_mask.empty()) {
@@ -652,6 +698,21 @@ void VlCoverpoint::sizedRangeW(WDataInP lop, WDataInP hip) {
data.m_sizedElements.push_back({data.read(lop), data.read(hip), {}});
}
// Warn that the bins 'name' are ignored, because of 'reason'. From an mtask, VL_WARN_MT reports
// after returning, so the text is kept for the program, once for all instances.
static void _vl_cov_warn_ignored(const char* file, int line, const char* reason,
const char* name) VL_MT_SAFE {
static VerilatedMutex s_mutex;
static std::set<std::string> s_texts; // Texts of the warnings
std::string text = std::string{reason} + "; bin '" + name + "' ignored";
const char* textp;
{
const VerilatedLockGuard lock{s_mutex};
textp = s_texts.insert(std::move(text)).first->c_str();
}
VL_WARN_MT(file, line, "", textp);
}
void VlCoverpoint::sizedFinish(VlCovBinKind kind, QData count, bool positive, uint32_t limit,
const char* name, const char* file, int line, int col) {
ValueData& data = *m_valuesp;
@@ -664,9 +725,9 @@ void VlCoverpoint::sizedFinish(VlCovBinKind kind, QData count, bool positive, ui
if (VL_UNLIKELY(!positive)) {
// An error, after which (+verilator+error+limit) the array has no bins
sized.m_elements.clear();
VL_PRINTF_MT("%%Error: %s:%d: Coverage bin array size must be a positive integer"
VL_PRINTF_MT("%%Error: %s:%d: Coverage bin array '%s' size must be a positive integer"
" (IEEE 1800-2023 19.5.1)\n",
file, line);
file, line, name);
VL_STOP_MT(file, line, "");
return;
}
@@ -684,14 +745,20 @@ void VlCoverpoint::sizedFinish(VlCovBinKind kind, QData count, bool positive, ui
const ValueData::Value& bins = fewer ? total : declared;
if (data.compare(bins, data.toPosition(std::min(limit, UINT32_MAX - m_total))) > 0) {
sized.m_elements.clear();
VL_WARN_MT(file, line, "",
"Coverage bin array needs more bins than --coverage-max-bins; bin ignored");
_vl_cov_warn_ignored(file, line,
"Coverage bin array needs more bins than --coverage-max-bins", name);
return;
}
sized.m_count = bins[0];
if (!sized.m_count) return;
sized.m_perBin = one;
if (!fewer) data.quotient(total, declared, sized.m_perBin);
sized.m_unit = data.compare(sized.m_perBin, one) == 0;
// Disjoint elements in value order hold a value in one at most, which a binary search finds
sized.m_sorted = true;
for (size_t i = 1; sized.m_sorted && i < sized.m_elements.size(); ++i) {
sized.m_sorted = data.less(sized.m_elements[i - 1].m_hi, sized.m_elements[i].m_lo);
}
assert(m_nextBase == m_total);
m_total += sized.m_count;
m_counts.resize(m_total, 0);
@@ -751,6 +818,170 @@ uint32_t VlCoverpoint::sizedEnd(uint32_t sized) const {
return data.m_first + data.m_count;
}
void VlCoverpoint::withBegin(VlCovBinGrouping grouping, uint32_t limit) {
ValueData& data = *m_valuesp;
ValueData::With& with = data.m_with;
with = ValueData::With{};
with.m_grouping = grouping;
with.m_limit = std::min(limit, UINT32_MAX - m_total); // Bins index with 32 bits
with.m_values = ValueData::Value{data.positionWords()};
std::vector<ValueData::SizedElement>& elements = data.m_sizedElements;
if (grouping != VlCovBinGrouping::Fixed) {
// One bin, or a bin per value, holds a set of values, so the filter tests each
// candidate once, in value order: then the values it keeps come in order, and the
// limits hold when checked as they come
std::vector<ValueData::Range> ranges;
for (ValueData::SizedElement& element : elements) {
ranges.push_back({std::move(element.m_lo), std::move(element.m_hi), {}});
}
data.normalize(ranges);
elements.clear();
for (ValueData::Range& range : ranges) {
elements.push_back({std::move(range.m_lo), std::move(range.m_hi), {}});
}
}
// The filter is evaluated for each candidate, so bound the construction time
ValueData::Value candidates{data.positionWords()};
for (const ValueData::SizedElement& element : elements) {
data.countValues(element.m_lo, element.m_hi, candidates);
}
with.m_candidates = data.compare(candidates, data.toPosition(ValueData::CANDIDATE_LIMIT)) > 0;
if (with.m_candidates) elements.clear();
}
bool VlCoverpoint::withNext() {
ValueData& data = *m_valuesp;
ValueData::With& with = data.m_with;
if (with.m_full || with.m_next == data.m_sizedElements.size()) return false;
++with.m_next;
return true;
}
QData VlCoverpoint::withLo() const {
const ValueData& data = *m_valuesp;
return ValueData::number(data.m_sizedElements[data.m_with.m_next - 1].m_lo);
}
void VlCoverpoint::withLoW(WDataOutP valuep) const {
const ValueData& data = *m_valuesp;
const ValueData::Value& value = data.m_sizedElements[data.m_with.m_next - 1].m_lo;
std::copy(value.begin(), value.end(), valuep.datap());
}
QData VlCoverpoint::withHi() const {
const ValueData& data = *m_valuesp;
return ValueData::number(data.m_sizedElements[data.m_with.m_next - 1].m_hi);
}
void VlCoverpoint::withHiW(WDataOutP valuep) const {
const ValueData& data = *m_valuesp;
const ValueData::Value& value = data.m_sizedElements[data.m_with.m_next - 1].m_hi;
std::copy(value.begin(), value.end(), valuep.datap());
}
bool VlCoverpoint::withRun(QData lo, QData hi) {
VlWide<VL_WQ_WORDS_E> low;
VlWide<VL_WQ_WORDS_E> high;
VL_SET_WQ(low, lo);
VL_SET_WQ(high, hi);
return withRunW(low, high);
}
bool VlCoverpoint::withRunW(WDataInP lop, WDataInP hip) {
ValueData& data = *m_valuesp;
ValueData::With& with = data.m_with;
ValueData::Value lo = data.read(lop);
ValueData::Value hi = data.read(hip);
const bool values = with.m_grouping == VlCovBinGrouping::Values;
if (values) data.countValues(lo, hi, with.m_values);
std::vector<ValueData::Range>& kept = with.m_kept;
ValueData::Value next;
if (!kept.empty()) {
next = kept.back().m_hi;
data.increment(next);
}
if (!kept.empty() && next == lo && data.less(kept.back().m_hi, lo)) {
kept.back().m_hi = std::move(hi); // Continues the last run
} else {
kept.push_back({std::move(lo), std::move(hi), {}});
}
// Bins each of a value, or runs of values, beyond the limit have too much memory. Neither
// count decreases as values come, so the bins are then ignored.
with.m_full = values ? data.compare(with.m_values, data.toPosition(with.m_limit)) > 0
: kept.size() > with.m_limit;
return !with.m_full;
}
void VlCoverpoint::withFinish(VlCovBinKind kind, QData count, bool positive, const char* name,
const char* file, int line, int col) {
ValueData& data = *m_valuesp;
assert(!data.m_frozen);
ValueData::With& with = data.m_with;
std::vector<ValueData::Range> kept;
kept.swap(with.m_kept);
data.m_sizedElements.clear();
const bool values = with.m_grouping == VlCovBinGrouping::Values;
if (VL_UNLIKELY(with.m_candidates || with.m_full)) {
kept.clear();
_vl_cov_warn_ignored(
file, line,
with.m_candidates ? "Coverage bin 'with' filter has more than 2**32 candidate values"
: values ? "Coverage bin array needs more bins than --coverage-max-bins"
: "Coverage bin 'with' filter keeps values in more ranges than"
" --coverage-max-bins",
name);
}
if (with.m_grouping == VlCovBinGrouping::Fixed) {
// Filtered first, then distributed (IEEE 1800-2023 19.5.1.1)
for (ValueData::Range& range : kept) {
data.m_sizedElements.push_back({std::move(range.m_lo), std::move(range.m_hi), {}});
}
sizedFinish(kind, count, positive, with.m_limit, name, file, line, col);
return;
}
data.m_sized.emplace_back();
ValueData::Sized& sized = data.m_sized.back();
sized.m_kind = kind;
sized.m_first = m_total;
if (kept.empty()) return; // No bin without a value (IEEE 1800-2023 19.11.1)
// The values, disjoint runs in order (see withBegin), of a bin each at its position, or of
// one bin
ValueData::Value position{data.positionWords()};
for (const ValueData::Range& range : kept) {
sized.m_elements.push_back({range.m_lo, range.m_hi, position});
if (data.m_bits <= VL_QUADSIZE) {
sized.m_fast.push_back({data.orderValue(ValueData::number(range.m_lo)),
data.orderValue(ValueData::number(range.m_hi)),
ValueData::number(position)});
}
if (values) data.countValues(range.m_lo, range.m_hi, position);
}
sized.m_count = values ? static_cast<uint32_t>(ValueData::number(position)) : 1;
sized.m_perBin = data.toPosition(1);
sized.m_fastPerBin = 1;
sized.m_sorted = true;
sized.m_unit = true;
assert(m_nextBase == m_total);
m_total += sized.m_count;
m_counts.resize(m_total, 0);
m_crossIdx.resize(m_total, -1);
data.m_values.resize(m_total);
addNamer(kind, sized.m_count, values ? VlCovBinNaming::Values : VlCovBinNaming::Single, name,
file, line, col);
// Give each bin its values, for exclusions and cross selections
uint32_t bin = sized.m_first;
for (ValueData::Range& range : kept) {
if (!values) {
data.m_values[bin].m_ranges.push_back(std::move(range));
continue;
}
for (ValueData::Value value = range.m_lo; true; data.increment(value)) {
data.m_values[bin++].m_ranges.push_back({value, value, {}});
if (value == range.m_hi) break;
}
}
}
bool VlCoverpoint::sizedSample(uint32_t sized, QData value, bool enabled) {
ValueData& data = *m_valuesp;
const ValueData::Sized& array = data.m_sized[sized];
@@ -760,8 +991,19 @@ bool VlCoverpoint::sizedSample(uint32_t sized, QData value, bool enabled) {
return sizedSampleW(sized, words, enabled);
}
const uint64_t ordered = data.orderValue(value);
auto beginIt = array.m_fast.begin();
auto endIt = array.m_fast.end();
if (array.m_sorted) {
// Of disjoint runs in value order, only the last one starting by the value may hold it
endIt = std::upper_bound(beginIt, endIt, ordered,
[](uint64_t candidate, const ValueData::SizedFast& run) {
return candidate < run.m_lo;
});
if (endIt != beginIt) beginIt = std::prev(endIt);
}
uint32_t last = UINT32_MAX; // No bin yet; bins index below UINT32_MAX
for (const ValueData::SizedFast& element : array.m_fast) {
for (auto it = beginIt; it != endIt; ++it) {
const ValueData::SizedFast& element = *it;
if (ordered < element.m_lo || ordered > element.m_hi) continue;
const uint64_t bin = (element.m_position + (ordered - element.m_lo)) / array.m_fastPerBin;
// Bins past the last hold none; it holds the remaining values
@@ -775,16 +1017,36 @@ bool VlCoverpoint::sizedSample(uint32_t sized, QData value, bool enabled) {
bool VlCoverpoint::sizedSampleW(uint32_t sized, WDataInP valuep, bool enabled) {
ValueData& data = *m_valuesp;
const ValueData::Sized& array = data.m_sized[sized];
auto beginIt = array.m_elements.begin();
auto endIt = array.m_elements.end();
if (array.m_sorted) {
// Of disjoint runs in value order, only the last one starting by the value may hold it
endIt = std::upper_bound(beginIt, endIt, valuep,
[&](WDataInP candidate, const ValueData::SizedElement& element) {
return data.less(candidate, element.m_lo);
});
if (endIt != beginIt) beginIt = std::prev(endIt);
}
uint32_t last = UINT32_MAX; // No bin yet; bins index below UINT32_MAX
for (const ValueData::SizedElement& element : array.m_elements) {
for (auto it = beginIt; it != endIt; ++it) {
const ValueData::SizedElement& element = *it;
if (data.less(valuep, element.m_lo) || data.less(element.m_hi, valuep)) continue;
ValueData::Value position = data.distance(ValueData::view(element.m_lo), valuep);
data.increase(position, element.m_position);
ValueData::Value bin{data.positionWords()};
data.quotient(position, array.m_perBin, bin);
const bool past = !data.narrow(bin) || ValueData::number(bin) >= array.m_count;
uint64_t bin = UINT64_MAX; // Past the last bin
if (array.m_unit) {
// A value's position is its bin, below 2^33 (see m_unit): the sum of the element's
// position and the low 64 bits of the value's distance from the element's first value
bin = ValueData::number(element.m_position)
+ (VL_SET_QW(valuep) - ValueData::number(element.m_lo));
} else {
ValueData::Value position = data.distance(ValueData::view(element.m_lo), valuep);
data.increase(position, element.m_position);
ValueData::Value quotient{data.positionWords()};
data.quotient(position, array.m_perBin, quotient);
if (data.narrow(quotient)) bin = ValueData::number(quotient);
}
// Bins past the last hold none; it holds the remaining values
sizedHit(array.m_kind,
array.m_first + (past ? array.m_count - 1 : static_cast<uint32_t>(bin[0])),
array.m_first + static_cast<uint32_t>(std::min<uint64_t>(bin, array.m_count - 1)),
enabled, last);
}
return last != UINT32_MAX;
@@ -847,6 +1109,8 @@ std::string VlCoverpoint::declaredBinName(uint32_t bin) const {
name += '[' + std::to_string(bin - nm.base()) + ']';
} else if (nm.naming() == VlCovBinNaming::Numbered) {
name += '_' + std::to_string(bin - nm.base());
} else if (nm.naming() == VlCovBinNaming::Values) {
name += '[' + m_valuesp->valueName(bin) + ']';
}
return name;
}
+26
View File
@@ -51,6 +51,14 @@ enum class VlCovBinNaming : uint8_t {
Single, // "<name>" one bin
Array, // "<name>[i]" bins b[N] value array
Numbered, // "<name>_<i>" automatic bins of a coverpoint without bins
Values, // "<name>[v]" bins b[] with a 'with' filter, a bin per value v
};
// How the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1) hold the values it keeps
enum class VlCovBinGrouping : uint8_t {
Single, // bins b = ...: one bin
Values, // bins b[] = ...: a bin for each value, in value order
Fixed, // bins b[N] = ...: distributed over N bins, as a sized array's
};
// Specifies the naming scheme for a range of bins, allowing the
@@ -192,6 +200,24 @@ public:
/// sizedFinish() order, for cross selections.
uint32_t sizedFirst(uint32_t sized) const;
uint32_t sizedEnd(uint32_t sized) const;
/// Begin the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1), whose candidates are the
/// values sizedRange() added, and whose bins then count as a sized array's. At most
/// 'limit' bins, or runs of values kept.
void withBegin(VlCovBinGrouping grouping, uint32_t limit);
/// Advance to the next run of candidates, withLo() to withHi(); false after the last, or
/// once too many values are kept
bool withNext();
QData withLo() const;
void withLoW(WDataOutP valuep) const;
QData withHi() const;
void withHiW(WDataOutP valuep) const;
/// Keep the values lo..hi, in the order the filter kept them; false once too many are
bool withRun(QData lo, QData hi);
bool withRunW(WDataInP lop, WDataInP hip);
/// Make the bins of the values kept, a sized array 'name[count]' for Fixed grouping (see
/// sizedFinish())
void withFinish(VlCovBinKind kind, QData count, bool positive, const char* name,
const char* file, int line, int col);
// ---- hot path (from generated sample()) ----
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
+9
View File
@@ -915,6 +915,15 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
macro(COVERGROUP_VALUE_TRANSITIONS, "valueTransitions", false, "r") \
macro(COVERGROUP_VALUE_TYPE, "valueType", false, "rr") \
macro(COVERGROUP_WEIGHT, "weight", false, "rr") \
macro(COVERGROUP_WITH_BEGIN, "withBegin", false, "rr") \
macro(COVERGROUP_WITH_FINISH, "withFinish", false, "r+") \
macro(COVERGROUP_WITH_HI, "withHi", false, "") \
macro(COVERGROUP_WITH_HI_W, "withHiW", false, "w") \
macro(COVERGROUP_WITH_LO, "withLo", false, "") \
macro(COVERGROUP_WITH_LO_W, "withLoW", false, "w") \
macro(COVERGROUP_WITH_NEXT, "withNext", false, "") \
macro(COVERGROUP_WITH_RUN, "withRun", false, "rr") \
macro(COVERGROUP_WITH_RUN_W, "withRunW", false, "rr") \
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
macro(DYN_CLEAR, "clear", false, "") \
+14
View File
@@ -1254,6 +1254,20 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverWith final : public AstNode {
// The values of a coverpoint bin for which a 'with' filter is true (IEEE 1800-2023
// 19.5.1.1), the only range list element of its AstCoverBin
// @astgen op1 := subp : List[AstNode] // Range list, or AstCoverpointRef for all values
// @astgen op2 := filterp : AstNodeExpr
// @astgen op3 := itemp : Optional[AstVar] // Candidate value 'item', from V3LinkParse
public:
AstCoverWith(FileLine* fl, AstNode* subp, AstNodeExpr* filterp)
: ASTGEN_SUPER_CoverWith(fl) {
addSubp(subp);
this->filterp(filterp);
}
ASTGEN_MEMBERS_AstCoverWith;
};
class AstCovergroup final : public AstNode {
// Represents a covergroup declaration. V3LinkParse transforms this
// into an AstClass with isCovergroup==true and attaches the clocking
+447 -123
View File
@@ -30,6 +30,7 @@
#include "V3Error.h"
#include "V3File.h"
#include "V3MemberMap.h"
#include "V3UniqueNames.h"
#include <bitset>
#include <cmath>
@@ -295,6 +296,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
std::deque<BinRun> runs; // Runs 'values' refers to
};
std::map<AstVar*, CoverpointBins> m_cpBins; // Runtime coverpoint -> binsof index ranges
// Names of the bins declarations each coverpoint ignored, which binsof selects as no bins
std::map<const AstCoverpoint*, std::vector<std::string>> m_droppedBins;
// Prefixes of the constructor temporaries of constructed bins, as coverpoint and bin names
// alone may repeat: coverpoint 'a_' bins 'b', and coverpoint 'a' bins '_b'
V3UniqueNames m_sizedNames{"__Vsized"};
std::vector<AstNodeExpr*> m_detachedValues; // Array-bin values m_cpBins refers to
std::set<AstCoverCross*>
m_droppedCrosses; // Crosses with a bare-variable item: drop (COVERIGN)
@@ -372,6 +378,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
m_crossedCpNames.clear();
m_cpVarMap.clear();
m_cpBins.clear();
m_droppedBins.clear();
m_sizedNames.reset();
m_runtimePoints.clear();
m_runtimeCrosses.clear();
m_excludedVars.clear();
@@ -400,12 +408,13 @@ class FunctionalCoverageVisitor final : public VNVisitor {
std::map<AstCoverpoint*, std::vector<AstCoverCross*>> consumers;
std::map<AstCoverCross*, std::vector<AstCoverpoint*>> inputs;
for (AstCoverpoint* const cpp : m_coverpoints) {
checkSizedArrays(cpp);
checkBinNames(cpp);
checkConstructedBins(cpp);
if (!cpp->exprp()->dtypep()->skipRefp()->isIntegralOrPacked()) continue;
// Bins without values leave the report (IEEE 1800-2023 19.11.1), exclusions or not.
// A sized array of bins gets its values when the covergroup is constructed.
// Constructed bins get their values when the covergroup is constructed.
if (!coverpointHasStateExclusions(cpp) && !coverpointHasEmptyBins(cpp)
&& !coverpointHasSizedArrays(cpp)) {
&& !coverpointHasConstructedBins(cpp)) {
continue;
}
m_runtimePoints.insert(cpp);
@@ -495,62 +504,176 @@ class FunctionalCoverageVisitor final : public VNVisitor {
return binp->arraySizep() && !isAutoBins(binp);
}
// 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. 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();
// The 'with' filter of a bin, if any (IEEE 1800-2023 19.5.1.1)
static AstCoverWith* binWith(const AstCoverBin* binp) {
return VN_CAST(binp->rangesp(), CoverWith);
}
// True for bins whose values the covergroup constructor computes: a sized array, or bins of
// the values a 'with' filter keeps
static bool isConstructedBins(const AstCoverBin* binp) {
return isSizedArray(binp) || binWith(binp);
}
// The range list of a bin's values, or of the candidates of its 'with' filter; null for all
// of the coverpoint's values, which a filter of the coverpoint's name has
static AstNode* binRangesp(const AstCoverBin* binp) {
const AstCoverWith* const withp = binWith(binp);
if (!withp) return binp->rangesp();
return VN_IS(withp->subp(), CoverpointRef) ? nullptr : withp->subp();
}
// Report and delete a bins declaration of a name that another of its coverpoint has
void checkBinNames(AstCoverpoint* coverpointp) {
std::set<std::string> names;
for (AstNode* nodep = coverpointp->binsp(); nodep;) {
AstCoverBin* const binp = VN_AS(nodep, CoverBin);
nodep = nodep->nextp();
if (!isSizedArray(binp)) continue;
AstNodeExpr* const sizep = binp->arraySizep();
const AstConst* const constp = VN_CAST(sizep, Const);
if (VN_IS(sizep, Unbounded)) { // A parameter of '$'; see bins_orBraE
binp->v3error("Bins array size must be integral, not '$' (IEEE 1800-2023 19.5.1)");
} else if (!sizep->dtypep()->skipRefp()->isIntegralOrPacked()) {
sizep->v3error("Bins array size must be integral (IEEE 1800-2023 19.5.1)");
} else if (constp && (constp->num().isFourState() || binsCount(constp) < 1)) {
sizep->v3error("Bins array size must be >= 1, got "
<< (constp->num().isFourState() ? constp->num().ascii(false)
: constp->num().toDecimalS())
<< " (IEEE 1800-2023 19.5.1)");
} else if (!integral) {
binp->v3warn(COVERIGN, "Unsupported: 'bins' explicit array size of a real "
"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;
}
if (names.emplace(binp->name()).second) continue;
binp->v3error("Duplicate bin " << binp->prettyNameQ() << " in coverpoint "
<< coverpointp->prettyNameQ()
<< " (IEEE 1800-2023 3.13)");
VL_DO_DANGLING(pushDeletep(binp->unlinkFrBack()), binp);
}
}
// The ranges of values the wildcard patterns of a sized wildcard array give, counted up to
// more than --coverage-max-bins
// Delete an ignored bins declaration, which binsof then selects as no bins
void dropBins(const AstCoverpoint* coverpointp, AstCoverBin* binp) {
m_droppedBins[coverpointp].push_back(binp->name());
VL_DO_DANGLING(pushDeletep(binp->unlinkFrBack()), binp);
}
// Check the size of a sized array of bins, which drops an invalid array. A real coverpoint's
// are unsupported, and treated as arrays of a bin per value. False unless it stays sized.
bool checkBinsArraySize(const AstCoverpoint* coverpointp, AstCoverBin* binp, bool integral) {
AstNodeExpr* const sizep = binp->arraySizep();
const AstConst* const constp = VN_CAST(sizep, Const);
if (VN_IS(sizep, Unbounded)) { // A parameter of '$'; see bins_orBraE
binp->v3error("Bins array size must be integral, not '$' (IEEE 1800-2023 19.5.1)");
} else if (!sizep->dtypep()->skipRefp()->isIntegralOrPacked()) {
sizep->v3error("Bins array size must be integral (IEEE 1800-2023 19.5.1)");
} else if (constp && (constp->num().isFourState() || binsCount(constp) < 1)) {
sizep->v3error("Bins array size must be >= 1, got "
<< (constp->num().isFourState() ? constp->num().ascii(false)
: constp->num().toDecimalS())
<< " (IEEE 1800-2023 19.5.1)");
} else if (!integral) {
binp->v3warn(COVERIGN, "Unsupported: 'bins' explicit array size of a real "
"coverpoint (treated as '[]')");
VL_DO_DANGLING(pushDeletep(sizep->unlinkFrBack()), sizep);
return false;
} else {
return true;
}
dropBins(coverpointp, binp);
return false;
}
// Check the bins of a coverpoint whose values the constructor computes, dropping invalid
// ones. A wildcard array of too many ranges of values is ignored, or if ignore or illegal,
// treated as one bin; filtering too many ranges of values ignores the bins.
void checkConstructedBins(AstCoverpoint* coverpointp) {
const bool integral = coverpointp->exprp()->dtypep()->skipRefp()->isIntegralOrPacked();
for (AstNode* nodep = coverpointp->binsp(); nodep;) {
AstCoverBin* const binp = VN_AS(nodep, CoverBin);
nodep = nodep->nextp();
if (!isConstructedBins(binp)) continue;
if (isSizedArray(binp) && !checkBinsArraySize(coverpointp, binp, integral)) continue;
if (!binp->isWildcard()
|| sizedWildcardRuns(binp, coverpointp->exprp())
<= v3Global.opt.coverageMaxBins()) {
if (binWith(binp) && withCandidatesOver(binp, coverpointp->exprp())) {
binp->v3warn(COVERIGN, "Unsupported: 'with' filter of more than 2**32 "
"candidate values; bin "
<< binp->prettyNameQ() << " ignored");
if (binp->binsType().binIsNormal()) coverpointp->user2(true);
dropBins(coverpointp, binp);
}
continue;
}
// An ignore or illegal array still excludes or checks its values, as one bin
const bool single = !binp->binsType().binIsNormal() && !binWith(binp);
binp->v3warn(COVERIGN,
"Unsupported: " << (binWith(binp) ? "'with' filter of wildcard '"
: "sized wildcard array '")
<< binp->binsType().verilogKwd()
<< "' of more than --coverage-max-bins of "
<< v3Global.opt.coverageMaxBins()
<< " ranges of values; bin " << binp->prettyNameQ()
<< (single ? " treated as one bin" : " ignored") << "\n"
<< binp->warnMore()
<< "... Suggest a larger --coverage-max-bins");
if (single) {
AstNodeExpr* const sizep = binp->arraySizep();
VL_DO_DANGLING(pushDeletep(sizep->unlinkFrBack()), sizep);
binp->isArray(false);
continue;
}
if (binp->binsType().binIsNormal()) coverpointp->user2(true);
dropBins(coverpointp, binp);
}
}
// True if a 'with' filter would be evaluated for more than 2**32 candidate values, known
// now for the coverpoint's name, or for a range list of constants: each is evaluated once
// but for an array 'b[N]', which keeps their order and duplicates (see withBegin())
static bool withCandidatesOver(AstCoverBin* binp, AstNodeExpr* exprp) {
const int width = runWidth(exprp);
std::vector<std::pair<V3Number, V3Number>> runs;
if (!binRangesp(binp)) runs = coverpointValues(binp, exprp);
const auto constant
= [](const AstNode* nodep) { return VN_IS(nodep, Const) || VN_IS(nodep, Unbounded); };
for (AstNode* rangep = binRangesp(binp); rangep; rangep = rangep->nextp()) {
const AstInsideRange* const irp = VN_CAST(rangep, InsideRange);
// Else known when constructed
if (irp ? !constant(irp->lhsp()) || !constant(irp->rhsp()) : !VN_IS(rangep, Const)) {
return false;
}
CrossValueRange range{rangep, resolveWidth(rangep, exprp)};
if (!resolveValue(rangep, exprp, true, binp->isWildcard(), range)
|| crossRangeEmpty(range)) {
continue;
}
std::vector<std::pair<V3Number, V3Number>> found{{range.lo, range.hi}};
if (range.wildcard) { // checkConstructedBins bounded the runs
found.clear();
crossRangeRuns(range, v3Global.opt.coverageMaxBins(), found);
}
for (const std::pair<V3Number, V3Number>& run : found) {
runs.emplace_back(V3Number{rangep, width, run.first},
V3Number{rangep, width, run.second});
}
}
if (!isSizedArray(binp)) { // The union of the values
std::sort(runs.begin(), runs.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>& run : runs) {
if (merged.empty() || crossValueLess(merged.back().second, run.first)) {
merged.push_back(run);
} else if (crossValueLess(merged.back().second, run.second)) {
merged.back().second = run.second;
}
}
runs = std::move(merged);
}
uint64_t count = 0;
for (const std::pair<V3Number, V3Number>& run : runs) {
V3Number span{exprp, width};
span.opSub(run.second, run.first);
if (span.mostSetBitP1() > 32) return true; // More than 2**32 values
count += span.toUQuad() + 1;
if (count > (uint64_t{1} << 32)) return true;
}
return false;
}
// The ranges of values the wildcard patterns of a sized wildcard array, or of a 'with'
// filter's candidates, 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()) {
for (AstNode* rangep = binRangesp(binp); 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)) {
@@ -797,8 +920,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|| cbinp->binsType() == VCoverBinsType::BINS_IGNORE
|| cbinp->binsType() == VCoverBinsType::BINS_ILLEGAL)
continue;
// The values of a sized array are known at construction; see emitSizedSample
if (isSizedArray(cbinp)) continue;
// The values of constructed bins are known at construction; see emitSizedSample
if (isConstructedBins(cbinp)) continue;
if (isAutoBins(cbinp)) {
// Automatic bins partition the whole domain, leaving no default value
if (anyBinMatchp) VL_DO_DANGLING(pushDeletep(anyBinMatchp), anyBinMatchp);
@@ -828,9 +951,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
return false;
}
static bool coverpointHasSizedArrays(const AstCoverpoint* coverpointp) {
static bool coverpointHasConstructedBins(const AstCoverpoint* coverpointp) {
for (const AstNode* nodep = coverpointp->binsp(); nodep; nodep = nodep->nextp()) {
if (isSizedArray(VN_AS(nodep, CoverBin))) return true;
if (isConstructedBins(VN_AS(nodep, CoverBin))) return true;
}
return false;
}
@@ -842,8 +965,9 @@ 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;
// A wildcard array has a bin for each value its elements match, so none empty, and
// the constructor creates no bin without values of a 'with' filter
if ((binp->isArray() && binp->isWildcard()) || binWith(binp)) continue;
bool empty = true;
for (AstNode* valuep = binp->rangesp(); valuep; valuep = valuep->nextp()) {
if (binp->isArray() && VN_IS(valuep, InsideRange)) continue;
@@ -1001,6 +1125,17 @@ class FunctionalCoverageVisitor final : public VNVisitor {
bins.push_back({{0, maxVal}});
return exprp->width() <= 64;
}
if (binWith(cbinp)) {
// A value is in one bin of a filter's, but of a sized array, in one for each range
// list element holding it; the coverpoint's name is one element
int elements = 0;
for (const AstNode* rp = binRangesp(cbinp); rp && isSizedArray(cbinp);
rp = rp->nextp()) {
++elements;
}
slotCount += std::max(1, elements);
return false;
}
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)
@@ -1203,8 +1338,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
arrayBinp->v3warn(COVERIGN,
"Unsupported: array 'bins' of a real coverpoint "
"covering more than "
<< v3Global.opt.coverageMaxRealBins()
<< " values; bin ignored.\n"
<< v3Global.opt.coverageMaxRealBins() << " values; bin "
<< arrayBinp->prettyNameQ() << " ignored.\n"
<< arrayBinp->warnMore()
<< "... Suggest a larger --coverage-max-real-bins");
unsupportedOut = true;
@@ -1285,7 +1420,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
arrayBinp->v3warn(COVERIGN, "Unsupported: array 'bins' covering more than "
<< v3Global.opt.coverageMaxBins()
<< " values (e.g. an open '[lo:$]' range over "
"a wide coverpoint); bin ignored\n"
"a wide coverpoint); bin "
<< arrayBinp->prettyNameQ() << " ignored\n"
<< arrayBinp->warnMore()
<< "... Suggest a larger --coverage-max-bins");
out.runs.clear();
@@ -1361,15 +1497,15 @@ class FunctionalCoverageVisitor final : public VNVisitor {
// 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");
arrayBinp->v3warn(
COVERIGN, "Unsupported: wildcard array '"
<< arrayBinp->binsType().verilogKwd()
<< "' of more than --coverage-max-bins of "
<< v3Global.opt.coverageMaxBins() << " values; bin "
<< arrayBinp->prettyNameQ()
<< (out.single ? " treated as one bin" : " ignored") << "\n"
<< arrayBinp->warnMore()
<< "... Suggest a larger --coverage-max-bins");
}
out.unsupported = true;
return out;
@@ -1539,16 +1675,31 @@ class FunctionalCoverageVisitor final : public VNVisitor {
void emitSizedSample(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* cpVarp,
AstNodeExpr* exprp, uint32_t sized, AstVar* matchedp) {
FileLine* const fl = binp->fileline();
AstNodeExpr* const enabledp
AstNodeExpr* enabledp
= binCondition(coverpointp, binp, cpVarp, new AstConst{fl, AstConst::BitTrue{}});
UASSERT_OBJ(m_sampleFuncp, binp, "sample() CFunc not set for coverpoint");
const bool illegal = binp->binsType() == VCoverBinsType::BINS_ILLEGAL;
if (illegal) {
// The illegal action reads the condition too, which is evaluated once, as the guard
// may have side effects
AstVar* const varp
= new AstVar{fl, VVarType::BLOCKTEMP,
"__VcpEnabled_" + sanitizeGeneratedName(coverpointp->name()) + "_"
+ cvtToStr(sized),
binp->findBitDType()};
varp->funcLocal(true);
m_sampleFuncp->addStmtsp(varp);
m_sampleFuncp->addStmtsp(
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, enabledp});
enabledp = new AstVarRef{fl, varp, VAccess::READ};
}
AstCMethodHard* const callp
= itemCall(fl, cpVarp,
exprp->isWide() ? VCMethod::COVERGROUP_SIZED_SAMPLE_W
: VCMethod::COVERGROUP_SIZED_SAMPLE,
{cnum(fl, sized), exprp->cloneTree(false), enabledp});
callp->dtypeSetBit();
UASSERT_OBJ(m_sampleFuncp, binp, "sample() CFunc not set for coverpoint");
if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) {
if (illegal) {
m_sampleFuncp->addStmtsp(
new AstIf{fl, new AstLogAnd{fl, callp, enabledp->cloneTree(false)},
makeIllegalBinAction(fl, "Illegal bin " + binp->prettyNameQ()
@@ -1585,61 +1736,223 @@ class FunctionalCoverageVisitor final : public VNVisitor {
return valuep;
}
// Emit the constructor code building the sized array of bins 'binp': the values of each
// element that are coverpoint values (IEEE 1800-2023 19.5.7), then its bins
void generateSizedArray(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* cpVarp,
AstNodeExpr* exprp) {
// The values of a coverpoint, which its name denotes (IEEE 1800-2023 19.5.1.1), as runs in
// value order: an enumerated type's values (6.19), else all values of its type. Bounds
// are at runWidth(), sign-extended like a CrossValueRange's.
static std::vector<std::pair<V3Number, V3Number>> coverpointValues(AstNode* nodep,
AstNodeExpr* exprp) {
const int width = runWidth(exprp);
std::vector<std::pair<V3Number, V3Number>> runs;
const AstEnumDType* const enump = VN_CAST(exprp->dtypep()->skipRefToEnump(), EnumDType);
if (!enump) {
const CrossValueRange domain
= crossValueDomain(nodep, exprp->width(), exprp->isSigned(), width);
runs.emplace_back(domain.lo, domain.hi);
return runs;
}
std::vector<V3Number> values;
for (const AstEnumItem* itemp = enump->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), EnumItem)) {
const V3Number& num = VN_AS(itemp->valuep(), Const)->num();
if (num.isFourState()) continue; // Not a coverpoint value (19.5.7)
values.emplace_back(nodep, width);
if (exprp->isSigned()) {
values.back().opExtendS(num, num.width());
} else {
values.back().opAssign(num);
}
}
std::sort(values.begin(), values.end(), crossValueLess);
const V3Number one{nodep, width, 1};
for (const V3Number& value : values) {
V3Number next{nodep, width};
if (!runs.empty() && next.opAdd(runs.back().second, one).isCaseEq(value)) {
runs.back().second = value;
} else {
runs.emplace_back(value, value);
}
}
return runs;
}
// Emit the constructor code building the bins 'binp' whose values it computes: the values of
// each element that are coverpoint values (IEEE 1800-2023 19.5.7), those a 'with' filter
// keeps (19.5.1.1), then its bins
void generateConstructedBins(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* cpVarp,
AstNodeExpr* exprp) {
FileLine* const fl = binp->fileline();
const string prefix
= "__Vsized_" + sanitizeGeneratedName(coverpointp->name() + "__" + binp->name());
AstNodeExpr* const sizep = binp->arraySizep();
AstVar* const countp = constructorTemp(fl, prefix + "_count", sizep->dtypep());
m_constructorp->addStmtsp(
new AstAssign{fl, new AstVarRef{fl, countp, VAccess::WRITE}, sizep->cloneTree(false)});
= m_sizedNames.get(sanitizeGeneratedName(coverpointp->name() + "__" + binp->name()));
AstVar* countp = nullptr;
if (AstNodeExpr* const sizep = binp->arraySizep()) {
countp = constructorTemp(fl, prefix + "_count", sizep->dtypep());
m_constructorp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, countp, VAccess::WRITE},
sizep->cloneTree(false)});
}
AstCoverWith* const withp = binWith(binp);
if (withp && !binRangesp(binp)) { // The coverpoint's name: all of its values
for (const std::pair<V3Number, V3Number>& run : coverpointValues(binp, exprp)) {
m_constructorp->addStmtsp(itemCall(fl, cpVarp,
exprp->isWide()
? VCMethod::COVERGROUP_SIZED_RANGE_W
: VCMethod::COVERGROUP_SIZED_RANGE,
{newValueConst(fl, run.first, exprp),
newValueConst(fl, run.second, exprp)})
->makeStmt());
}
}
uint32_t element = 0;
for (AstNode* rangep = binp->rangesp(); rangep; rangep = rangep->nextp()) {
for (AstNode* rangep = binRangesp(binp); rangep; rangep = rangep->nextp()) {
if (VN_IS(rangep, Unbounded)) { // A parameter of '$'
binp->v3error("Bins value may not be '$', which may only bound a range "
"(IEEE 1800-2023 6.20.7)");
continue;
}
generateSizedElement(cpVarp, rangep, exprp, binp->isWildcard(),
prefix + "_" + cvtToStr(element++));
generateSizedElement(cpVarp, binp, rangep, exprp, prefix + "_" + cvtToStr(element++));
}
const auto countRef = [&]() { return new AstVarRef{fl, countp, VAccess::READ}; };
AstConst* const zerop = new AstConst{fl, AstConst::DTyped{}, countp->dtypep()};
AstNodeExpr* const positivep
= countp->isSigned() ? static_cast<AstNodeExpr*>(new AstGtS{fl, countRef(), zerop})
: new AstNeq{fl, countRef(), zerop};
// Saturate a count wider than 64 bits: min(N, T) is unchanged, or over any bins limit
AstNodeExpr* countValuep = resizeValue(countRef(), countp->findUInt64DType());
if (countp->width() > VL_QUADSIZE) {
countValuep = new AstCond{
fl,
new AstRedOr{
fl, new AstSel{fl, countRef(), VL_QUADSIZE, countp->width() - VL_QUADSIZE}},
new AstConst{fl, AstConst::Unsized64{}, std::numeric_limits<uint64_t>::max()},
countValuep};
countValuep->dtypeSetUInt64();
if (withp) generateWithFilter(binp, withp, cpVarp, exprp, prefix);
AstNodeExpr* countValuep;
AstNodeExpr* positivep;
if (countp) {
const auto countRef = [&]() { return new AstVarRef{fl, countp, VAccess::READ}; };
AstConst* const zerop = new AstConst{fl, AstConst::DTyped{}, countp->dtypep()};
positivep = countp->isSigned()
? static_cast<AstNodeExpr*>(new AstGtS{fl, countRef(), zerop})
: new AstNeq{fl, countRef(), zerop};
// Saturate a count wider than 64 bits: min(N, T) is unchanged, or over any limit
countValuep = resizeValue(countRef(), countp->findUInt64DType());
if (countp->width() > VL_QUADSIZE) {
countValuep = new AstCond{
fl,
new AstRedOr{fl, new AstSel{fl, countRef(), VL_QUADSIZE,
countp->width() - VL_QUADSIZE}},
new AstConst{fl, AstConst::Unsized64{}, std::numeric_limits<uint64_t>::max()},
countValuep};
countValuep->dtypeSetUInt64();
}
} else { // Of a filter's scalar bin, or bin per value
countValuep = new AstConst{fl, AstConst::Unsized64{}, 1};
positivep = new AstConst{fl, AstConst::BitTrue{}};
}
std::vector<AstNodeExpr*> args{ctext(fl, binp->binsType().binSetEnum()), countValuep,
positivep};
// A filter's bins have the limit it began with
if (!withp) args.push_back(cnum(fl, v3Global.opt.coverageMaxBins()));
const bool prot = v3Global.opt.protectIds();
args.push_back(ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot))));
args.push_back(ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))));
args.push_back(cnum(fl, static_cast<uint32_t>(fl->lineno())));
args.push_back(cnum(fl, static_cast<uint32_t>(fl->firstColumn())));
m_constructorp->addStmtsp(
itemCall(fl, cpVarp, VCMethod::COVERGROUP_SIZED_FINISH,
{ctext(fl, binp->binsType().binSetEnum()), countValuep, positivep,
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())),
cnum(fl, static_cast<uint32_t>(fl->firstColumn()))})
itemCall(fl, cpVarp,
withp ? VCMethod::COVERGROUP_WITH_FINISH : VCMethod::COVERGROUP_SIZED_FINISH,
args)
->makeStmt());
}
// 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. 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) {
// Emit the constructor code evaluating the 'with' filter of 'binp' for each of its candidate
// values, which sizedRange() added, passing the runs of values it keeps (IEEE 1800-2023
// 19.5.1.1). The filter is evaluated in a loop, whose code does not grow with the elements:
// withBegin(grouping, limit);
// more = 1;
// while (withNext()) {
// value = withLo(); last = withHi(); run = 0;
// while (true) {
// item = value;
// if (filter) { if (!run) { first = value; run = 1; } }
// else if (run) { more = withRun(first, value - 1); run = 0; }
// if (!more || value == last) break;
// ++value;
// }
// if (run) more = withRun(first, last);
// }
void generateWithFilter(AstCoverBin* binp, AstCoverWith* withp, AstVar* cpVarp,
AstNodeExpr* exprp, const string& prefix) {
FileLine* const fl = withp->fileline();
const bool wide = exprp->isWide();
const string grouping = !binp->isArray() ? "Single"
: binp->arraySizep() ? "Fixed"
: "Values";
m_constructorp->addStmtsp(itemCall(fl, cpVarp, VCMethod::COVERGROUP_WITH_BEGIN,
{ctext(fl, "VlCovBinGrouping::" + grouping),
cnum(fl, v3Global.opt.coverageMaxBins())})
->makeStmt());
// The candidates count in the coverpoint's width, and the filter reads each as 'item',
// of the coverpoint's type, so that a filter changing 'item' cannot change the loop
AstNodeDType* const valueDTypep
= exprp->findLogicDType(exprp->width(), exprp->width(),
exprp->isSigned() ? VSigning::SIGNED : VSigning::UNSIGNED);
AstVar* const valuep = constructorTemp(fl, prefix + "_value", valueDTypep);
AstVar* const lastp = constructorTemp(fl, prefix + "_last", valueDTypep);
AstVar* const firstp = constructorTemp(fl, prefix + "_first", valueDTypep);
AstVar* const runp = constructorTemp(fl, prefix + "_run", binp->findBitDType());
AstVar* const morep = constructorTemp(fl, prefix + "_more", binp->findBitDType());
AstVar* const itemp = withp->itemp()->unlinkFrBack();
itemp->name(prefix + "_item");
m_constructorp->addStmtsp(itemp);
const auto ref = [&](AstVar* varp) { return new AstVarRef{fl, varp, VAccess::READ}; };
const auto assign = [&](AstVar* varp, AstNodeExpr* rhsp) -> AstNode* {
return new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, rhsp};
};
const auto flag = [&](AstVar* varp, bool value) {
return assign(varp, value ? new AstConst{fl, AstConst::BitTrue{}}
: new AstConst{fl, AstConst::BitFalse{}});
};
const auto bound = [&](VCMethod narrow, VCMethod wideMethod, AstVar* varp) -> AstNode* {
if (wide) {
return itemCall(fl, cpVarp, wideMethod, {new AstVarRef{fl, varp, VAccess::WRITE}})
->makeStmt();
}
AstCMethodHard* const callp = itemCall(fl, cpVarp, narrow);
callp->dtypeSetUInt64();
return assign(varp, resizeValue(callp, valueDTypep));
};
const auto keep = [&](AstNodeExpr* lop, AstNodeExpr* hip) {
AstCMethodHard* const callp = itemCall(
fl, cpVarp, wide ? VCMethod::COVERGROUP_WITH_RUN_W : VCMethod::COVERGROUP_WITH_RUN,
{lop, hip});
callp->dtypeSetBit();
return assign(morep, callp);
};
const auto step = [&](bool up) {
AstConst* const onep = new AstConst{fl, AstConst::WidthedValue{}, exprp->width(), 1};
AstNodeExpr* const stepp
= up ? static_cast<AstNodeExpr*>(new AstAdd{fl, ref(valuep), onep})
: new AstSub{fl, ref(valuep), onep};
stepp->dtypep(valueDTypep);
return stepp;
};
AstLoop* const innerp = new AstLoop{fl};
innerp->addStmtsp(assign(itemp, ref(valuep)));
innerp->addStmtsp(new AstIf{
fl, withp->filterp()->unlinkFrBack(),
new AstIf{fl, new AstNot{fl, ref(runp)},
assign(firstp, ref(valuep))->addNext(flag(runp, true))},
new AstIf{fl, ref(runp), keep(ref(firstp), step(false))->addNext(flag(runp, false))}});
innerp->addStmtsp(new AstLoopTest{
fl, innerp, new AstLogAnd{fl, ref(morep), new AstNeq{fl, ref(valuep), ref(lastp)}}});
innerp->addStmtsp(assign(valuep, step(true)));
AstLoop* const outerp = new AstLoop{fl};
AstCMethodHard* const nextp = itemCall(fl, cpVarp, VCMethod::COVERGROUP_WITH_NEXT);
nextp->dtypeSetBit();
outerp->addStmtsp(new AstLoopTest{fl, outerp, nextp});
outerp->addStmtsp(
bound(VCMethod::COVERGROUP_WITH_LO, VCMethod::COVERGROUP_WITH_LO_W, valuep));
outerp->addStmtsp(
bound(VCMethod::COVERGROUP_WITH_HI, VCMethod::COVERGROUP_WITH_HI_W, lastp));
outerp->addStmtsp(flag(runp, false));
outerp->addStmtsp(innerp);
outerp->addStmtsp(new AstIf{fl, ref(runp), keep(ref(firstp), ref(lastp))});
m_constructorp->addStmtsp(flag(morep, true));
m_constructorp->addStmtsp(outerp);
}
// Emit 'sizedRange(lo, hi)' for the coverpoint values of an element of bins 'binp', whose
// values the constructor computes: resolved now if constant, else when constructed by
// clipping to the coverpoint's values. A wildcard pattern's values give a range for each
// run of them. 'prefix' names its temporaries.
void generateSizedElement(AstVar* cpVarp, const AstCoverBin* binp, AstNode* rangep,
AstNodeExpr* exprp, const string& prefix) {
FileLine* const fl = rangep->fileline();
const VCMethod method = exprp->isWide() ? VCMethod::COVERGROUP_SIZED_RANGE_W
: VCMethod::COVERGROUP_SIZED_RANGE;
@@ -1659,17 +1972,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
return constp && constp->num().isFourState();
};
if (irp && (fourState(lowp) || fourState(highp))) {
rangep->v3error("Four-state (x/z) value in array bins range bound; range bounds "
"must be two-state constants");
rangep->v3error("Four-state (x/z) value in "
<< (binp->isArray() ? "array bins" : "bin")
<< " range bound; range bounds must be two-state constants");
return;
}
CrossValueRange range{rangep, resolveWidth(rangep, exprp)};
if (!resolveValue(rangep, exprp, true, wildcard, range)) {
if (!resolveValue(rangep, exprp, true, binp->isWildcard(), range)) {
rangep->v3warn(E_UNSUPPORTED, "Unsupported: non-integral value in a coverage bin "
"of an integral coverpoint.");
} else if (!crossRangeEmpty(range)) {
std::vector<std::pair<V3Number, V3Number>> runs{{range.lo, range.hi}};
if (range.wildcard) { // checkSizedArrays bounded the runs
if (range.wildcard) { // checkConstructedBins bounded the runs
runs.clear();
crossRangeRuns(range, v3Global.opt.coverageMaxBins(), runs);
}
@@ -1864,8 +2178,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
}
// Walk bins (non-default, then default), assigning sequential indices that match the
// namer append order; emit sample increments and collect namer statements. A sized
// array's bins follow them all, placed when the coverpoint is constructed.
// namer append order; emit sample increments and collect namer statements. Constructed
// bins follow them all, placed when the coverpoint is constructed.
std::vector<AstNodeStmt*> namerStmts;
std::vector<AstCoverBin*> defaultBins;
std::vector<AstCoverBin*> sizedBins;
@@ -1879,8 +2193,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
defaultBins.push_back(cbinp);
continue;
}
if (isSizedArray(cbinp)) {
UASSERT_OBJ(dynamic, cbinp, "Sized bin array without value metadata");
if (isConstructedBins(cbinp)) {
UASSERT_OBJ(dynamic, cbinp, "Constructed bins without value metadata");
BinSpan span;
span.sized = static_cast<int32_t>(sizedBins.size());
m_cpBins.at(cpVarp).spans.emplace(cbinp->name(), span);
@@ -1910,7 +2224,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
if (binRunsFor(cbinp, exprp, plan)) {
// Array elements and automatic bins generate as runs, so neither sample() nor
// the constructor grows with their number of bins.
if (plan.unsupported) continue; // bin ignored or invalid; reserve no slot
if (plan.unsupported) { // bin ignored or invalid; reserve no slot
m_droppedBins[coverpointp].push_back(cbinp->name());
continue;
}
CoverpointBins& bins = m_cpBins.at(cpVarp);
const uint32_t firstValue = bins.total;
const uint32_t firstDeclared = static_cast<uint32_t>(idx);
@@ -1942,7 +2259,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
UASSERT_OBJ(!dynamic, cbinp, "Runtime value metadata for a real coverpoint");
bool unsupported = false;
std::vector<AstNodeExpr*> values = extractArrayValues(cbinp, exprp, unsupported);
if (unsupported) continue; // bin ignored (COVERIGN emitted); reserve no slot
if (unsupported) { // bin ignored (COVERIGN emitted); reserve no slot
m_droppedBins[coverpointp].push_back(cbinp->name());
continue;
}
namerStmts.push_back(makeNamer(cpVarp, cbinp, static_cast<int64_t>(values.size()),
static_cast<uint32_t>(idx), values));
for (AstNodeExpr* valuep : values) {
@@ -1966,7 +2286,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
++idx;
}
}
// A value a sized array's bin holds is no default bin's; only sampling tells which do
// A cross selecting an ignored bins declaration selects no bins
for (const std::string& name : m_droppedBins[coverpointp]) {
m_cpBins.at(cpVarp).spans.emplace(name, BinSpan{});
}
// A value a constructed bin holds is no default bin's; only sampling tells which do
AstVar* sizedMatchedp = nullptr;
if (!defaultBins.empty()
&& std::any_of(sizedBins.begin(), sizedBins.end(), [](const AstCoverBin* binp) {
@@ -2043,7 +2367,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
emitValueList(fl, cpVarp, VCMethod::COVERGROUP_VALUE_PATTERNS, lists.m_patterns);
emitValueList(fl, cpVarp, VCMethod::COVERGROUP_VALUE_TRANSITIONS, lists.m_transitions);
for (AstCoverBin* const binp : sizedBins) {
generateSizedArray(coverpointp, binp, cpVarp, exprp);
generateConstructedBins(coverpointp, binp, cpVarp, exprp);
}
m_constructorp->addStmtsp(
itemCall(fl, cpVarp, VCMethod::COVERGROUP_VALUE_FINALIZE)->makeStmt());
+12
View File
@@ -359,6 +359,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
if (setp != nodep->transp()) puts(", ");
iterateConst(setp);
}
} else if (VN_IS(nodep->rangesp(), CoverWith)) {
puts(" = ");
iterateConst(nodep->rangesp());
} else if (nodep->rangesp()) { // LCOV_EXCL_BR_LINE - false: CoverBin always has
// transp/rangesp/default
puts(" = {");
@@ -375,6 +378,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
puts(";\n");
}
void visit(AstCoverWith* nodep) override {
const bool rangeList = !VN_IS(nodep->subp(), CoverpointRef);
if (rangeList) putfs(nodep, "{");
iterateAndCommaConstNull(nodep->subp());
if (rangeList) puts("}");
puts(" with (");
iterateConst(nodep->filterp());
puts(")");
}
void visit(AstCoverBinsof* nodep) override {
putfs(nodep, nodep->isNegated() ? "!binsof(" : "binsof(");
iterateConst(nodep->pointp());
+24
View File
@@ -2319,6 +2319,17 @@ class LinkDotFindVisitor final : public VNVisitor {
nullptr);
iterateChildren(nodep);
}
void visit(AstCoverWith* nodep) override { // FindVisitor::
// The range list is outside the scope of the filter, which declares 'item'
iterateAndNextNull(nodep->subp());
VL_RESTORER(m_curSymp);
++m_modWithNum;
m_curSymp = m_statep->insertBlock(m_curSymp, "__VcoverWith" + cvtToStr(m_modWithNum),
nodep, m_classOrPackagep);
m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp));
iterateAndNextNull(nodep->itemp());
iterateAndNextNull(nodep->filterp());
}
void visit(AstNode* nodep) override { iterateChildren(nodep); } // FindVisitor::
@@ -5751,6 +5762,19 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->prodp(findProd(nodep, m_curSymp, nodep->name()));
iterateChildren(nodep);
}
void visit(AstCoverWith* nodep) override {
LINKDOT_VISIT_START();
UINFO(5, indent() << "visit " << nodep);
checkNoDot(nodep);
iterateAndNextNull(nodep->subp());
// The type of 'item' is that of the coverpoint expression, which may name 'item' too
iterateAndNextNull(nodep->itemp()->childDTypep());
VL_RESTORER(m_curSymp);
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
iterateAndNextNull(nodep->itemp());
iterateAndNextNull(nodep->filterp());
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
}
void visit(AstWith* nodep) override {
LINKDOT_VISIT_START();
UINFO(5, indent() << "visit " << nodep);
+42
View File
@@ -63,6 +63,7 @@ class LinkParseVisitor final : public VNVisitor {
AstNodeDType* m_dtypep = nullptr; // Current data type
AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew
AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew
AstCoverpoint* m_coverpointp = nullptr; // Current coverpoint
int m_anonUdpId = 0; // Counter for anonymous UDP instances
int m_coverpointNum = 0; // Counter for unnamed coverpoints within current covergroup
int m_genblkAbove = 0; // Begin block number of if/case/for above
@@ -1435,6 +1436,47 @@ class LinkParseVisitor final : public VNVisitor {
VL_DO_DANGLING(optp->deleteTree(), optp);
}
}
VL_RESTORER(m_coverpointp);
m_coverpointp = nodep;
iterateChildren(nodep);
}
void visit(AstCoverBin* nodep) override {
cleanFileline(nodep);
if (!m_coverpointp && VN_IS(nodep->rangesp(), CoverWith)) {
// A 'with' filter's candidates are of its coverpoint's type
nodep->rangesp()->v3warn(COVERIGN, "Unsupported: 'with' in cover bin outside a "
"coverpoint; bin "
<< nodep->prettyNameQ() << " ignored");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
iterateChildren(nodep);
}
void visit(AstCoverWith* nodep) override {
cleanFileline(nodep);
UASSERT_OBJ(m_coverpointp, nodep, "Bin 'with' filter outside a coverpoint");
if (const AstCoverpointRef* const refp = VN_CAST(nodep->subp(), CoverpointRef)) {
if (refp->name() != m_coverpointp->name()) {
refp->v3error("A bin 'with' filter may name only its own coverpoint "
<< m_coverpointp->prettyNameQ() << ", not " << refp->prettyNameQ()
<< " (IEEE 1800-2023 19.5.1.1)");
}
}
// The candidate value, of the coverpoint's type, which a filter need not read. The
// standard names it, so it hides another 'item' in the filter without a warning.
FileLine* const fl = nodep->fileline();
FileLine* const flNoWarn = new FileLine{fl};
flNoWarn->modifyWarnOff(V3ErrorCode::UNUSEDSIGNAL, true);
flNoWarn->modifyWarnOff(V3ErrorCode::VARHIDDEN, true);
AstVar* const varp = new AstVar{flNoWarn, VVarType::VAR, "item", VFlagChildDType{},
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{},
m_coverpointp->exprp()->cloneTree(false)}};
varp->funcLocal(true);
varp->noReset(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
nodep->itemp(varp);
iterateChildren(nodep);
}
+42
View File
@@ -2169,6 +2169,15 @@ class WidthVisitor final : public VNVisitor {
" type coverage is the weighted average of the"
" instances");
}
} else if (nodep->optType() == VCoverOptionType::DISTRIBUTE_FIRST) {
// A bins 'with' filter applies before the values are distributed to the bins
// (IEEE 1800-2023 19.5.1.1)
const AstConst* const constp = VN_CAST(nodep->valuep(), Const);
if (!constp || !constp->num().isEqZero()) {
nodep->v3warn(COVERIGN, "Ignoring unsupported: 'type_option.distribute_first';"
" 'with' filters apply before values are distributed"
" to bins");
}
}
// Add more options here as needed (goal, at_least, per_instance, comment)
@@ -2220,6 +2229,8 @@ class WidthVisitor final : public VNVisitor {
userIterate(rangep, nullptr);
fill(rangep->lhsp());
fill(rangep->rhsp());
} else if (VN_IS(itemp, CoverWith)) {
userIterate(itemp, m_vup);
} else {
itemp = userIterateSubtreeReturnEdits(itemp, WidthVP{SELF, BOTH}.p());
fill(V3Const::constifyEdit(itemp));
@@ -2242,6 +2253,37 @@ class WidthVisitor final : public VNVisitor {
}
userIterateAndNext(nodep->transp(), m_vup);
}
void visit(AstCoverWith* nodep) override {
// The candidate value 'item' has the coverpoint's type (IEEE 1800-2023 19.5.1.1)
userIterateAndNext(nodep->itemp(), nullptr);
if (!nodep->itemp()->dtypeSkipRefp()->isIntegralOrPacked()) {
nodep->v3error("Bin 'with' filters are not allowed on a coverpoint of a "
"non-integral expression (IEEE 1800-2023 19.5.1.1)");
}
if (!VN_IS(nodep->subp(), CoverpointRef)) {
widthCovergroupRanges(nodep->subp(), m_vup->dtypep()->width());
}
// The filter is true for a nonzero value (IEEE 1800-2023 12.4), of a type assignment
// compatible with an integral type (19.5.1.1)
AstNodeExpr* const filterp = VN_AS(
userIterateSubtreeReturnEdits(nodep->filterp(), WidthVP{SELF, BOTH}.p()), NodeExpr);
FileLine* const fl = filterp->fileline();
const AstNodeDType* const resultp = filterp->dtypep()->skipRefp();
if (resultp->isDouble()) {
VNRelinker relinker;
filterp->unlinkFrBack(&relinker);
relinker.relink(
new AstNeqD{fl, filterp, new AstConst{fl, AstConst::RealDouble{}, 0.0}});
} else if (!resultp->isIntegralOrPacked()) {
filterp->v3error("Bin 'with' filter must be assignment compatible with an integral "
"type, not "
<< resultp->prettyDTypeNameQ() << " (IEEE 1800-2023 19.5.1.1)");
filterp->replaceWith(new AstConst{fl, AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(filterp), filterp);
} else {
fixWidthReduce(filterp);
}
}
void visit(AstCoverTransSet* nodep) override { userIterateAndNext(nodep->itemsp(), m_vup); }
void visit(AstCoverTransItem* nodep) override {
widthCovergroupRanges(nodep->valuesp(), m_vup ? m_vup->dtypep()->width() : 0);
+15 -18
View File
@@ -7186,26 +7186,17 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, $6, false, true, false, $8}, $3); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ GRAMMARP->coverBinArrayUnsized($3);
AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$8, $6, $10}, false, false, false, $12}, $3); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ GRAMMARP->coverBinArrayUnsized($3);
AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$8, $6, $10}, true, false, false, $12}, $3); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ GRAMMARP->coverBinArrayUnsized($3);
AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$8, $6, $10}, false, true, false, $12}, $3); }
| yBINS 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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$<fl>5, *$5}, $8}, false, false, false, $10}, $3); }
| yIGNORE_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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$<fl>5, *$5}, $8}, true, false, false, $10}, $3); }
| 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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$<fl>5, *$5}, $8}, false, true, false, $10}, $3); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, $7, false, false, true, $9}, $4); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
@@ -7213,11 +7204,17 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = 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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$9, $7, $11}, false, false, true, $13}, $4); }
| yWILDCARD yIGNORE_BINS 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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$9, $7, $11}, true, false, true, $13}, $4); }
| yWILDCARD yILLEGAL_BINS 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); }
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$9, $7, $11}, false, true, true, $13}, $4); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$<fl>6, *$6}, $9}, false, false, true, $11}, $4); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$<fl>6, *$6}, $9}, true, false, true, $11}, $4); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = GRAMMARP->coverBinArray(new AstCoverBin{$<fl>3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$<fl>6, *$6}, $9}, false, true, true, $11}, $4); }
//
// // cgexpr part of trans_list
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
+4 -4
View File
@@ -82,7 +82,7 @@
: ... note: In instance 't'
70 | bins b_xz_arr_hi[] = {[4'h0:4'bzzzz]};
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:77:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin ignored
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:77:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin 'b_huge' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
77 | bins b_huge[] = {[0:$]};
@@ -215,12 +215,12 @@
: ... note: In instance 't'
196 | ignore_bins nonconstant = {size_var};
| ^~~~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:243:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin ignored
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:243:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin 'over_limit' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
243 | bins over_limit[] = {[0 : 1024]};
| ^~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:244:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin ignored
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:244:12: Unsupported: array 'bins' covering more than 1024 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin 'split_over_limit' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
244 | bins split_over_limit[] = {[0 : 511], [512 : 1024]};
@@ -233,7 +233,7 @@
: ... note: In instance 't'
253 | bins auto[4];
| ^~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:257:12: Unsupported: array 'bins' of a real coverpoint covering more than 1024 values; bin ignored.
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:257:12: Unsupported: array 'bins' of a real coverpoint covering more than 1024 values; bin 'over_limit' ignored.
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-real-bins
257 | bins over_limit[] = {[0 : 1024]};
@@ -0,0 +1,10 @@
%Error: t/t_covergroup_bins_dup_bad.v:14:12: Duplicate bin 'a' in coverpoint 'filtered' (IEEE 1800-2023 3.13)
: ... note: In instance 't'
14 | bins a = {[4 : 7]} with (item > 5);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_covergroup_bins_dup_bad.v:18:19: Duplicate bin 'a' in coverpoint 'kinds' (IEEE 1800-2023 3.13)
: ... note: In instance 't'
18 | ignore_bins a = {[4 : 7]};
| ^
%Error: Exiting due to
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()
@@ -0,0 +1,27 @@
// 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
// A bins declaration may not redeclare a bin name of its coverpoint (IEEE 1800-2023 3.13)
module t;
bit [3:0] v;
covergroup cg;
filtered: coverpoint v {
bins a = {[0 : 3]} with (item > 1);
bins a = {[4 : 7]} with (item > 5); // <--- Bad: duplicate name
}
kinds: coverpoint v {
bins a = {[0 : 3]};
ignore_bins a = {[4 : 7]}; // <--- Bad: duplicate name
}
// Bin names are those of their coverpoint
other: coverpoint v {
bins a = {[0 : 3]};
}
endgroup
cg inst = new;
initial $finish;
endmodule
@@ -25,7 +25,7 @@
: ... note: In instance 't'
27 | bins real_bound[2] = {[lo : 5]};
| ^
%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
%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 'runs' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
37 | wildcard bins runs[2] = {16'b????_????_????_???1};
@@ -46,7 +46,7 @@
: ... note: In instance 't'
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
%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; bin 'runs' treated as one bin
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
69 | wildcard illegal_bins runs[2] = {16'b????_????_????_???1};
@@ -1,11 +1,11 @@
%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:17:12: Unsupported: array 'bins' covering more than 4294967295 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin ignored
%Warning-COVERIGN: t/t_covergroup_max_bins_unsup.v:17:12: Unsupported: array 'bins' covering more than 4294967295 values (e.g. an open '[lo:$]' range over a wide coverpoint); bin 'over_limit' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
17 | bins over_limit[] = {[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.
%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
%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 'over_limit' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
21 | wildcard bins over_limit[] = {32'h????_????};
@@ -21,12 +21,12 @@
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:34: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 'over_limit' ignored.
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-real-bins
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
%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; bin 'over_limit' treated as one bin
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
38 | wildcard ignore_bins over_limit[] = {32'h????_????};
+8 -4
View File
@@ -11,16 +11,20 @@
: ... note: In instance 't'
18 | bins fraction[1.5] = {[0 : 3]};
| ^~~
%Error: t/t_covergroup_sized_bad.v:20:12: Bins array size must be integral, not '$' (IEEE 1800-2023 19.5.1)
%Error: t/t_covergroup_sized_bad.v:21:12: Bins array size must be integral, not '$' (IEEE 1800-2023 19.5.1)
: ... note: In instance 't'
20 | bins param_size[UNBOUNDED] = {[0 : 3]};
21 | bins param_size[UNBOUNDED] = {[0 : 3]};
| ^~~~~~~~~~
%Error: t/t_covergroup_sized_bad.v:19:29: Four-state (x/z) value in array bins range bound; range bounds must be two-state constants
: ... note: In instance 't'
19 | bins four_state[2] = {[4'b000x : 4'hf]};
| ^
%Error: t/t_covergroup_sized_bad.v:21:12: Bins value may not be '$', which may only bound a range (IEEE 1800-2023 6.20.7)
%Error: t/t_covergroup_sized_bad.v:20:31: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
: ... note: In instance 't'
21 | bins param_value[2] = {UNBOUNDED};
20 | bins four_state_with = {[4'b000x : 4'hf]} with (1);
| ^
%Error: t/t_covergroup_sized_bad.v:22:12: Bins value may not be '$', which may only bound a range (IEEE 1800-2023 6.20.7)
: ... note: In instance 't'
22 | bins param_value[2] = {UNBOUNDED};
| ^~~~~~~~~~~
%Error: Exiting due to
+2 -1
View File
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: CC0-1.0
// A sized array of bins needs a positive integral size (IEEE 1800-2023 19.5.1), and two-state
// range bounds, of which '$' may only be a whole bound (6.20.7)
// range bounds, as do bins with a 'with' filter, of which '$' may only be a whole bound (6.20.7)
module t;
logic [3:0] value;
@@ -17,6 +17,7 @@ module t;
bins negative[-1] = {[0 : 3]}; // <--- Bad: negative
bins fraction[1.5] = {[0 : 3]}; // <--- Bad: not integral
bins four_state[2] = {[4'b000x : 4'hf]}; // <--- Bad: x bound
bins four_state_with = {[4'b000x : 4'hf]} with (1); // <--- Bad: x bound
bins param_size[UNBOUNDED] = {[0 : 3]}; // <--- Bad: '$'
bins param_value[2] = {UNBOUNDED}; // <--- Bad: '$'
}
@@ -1,6 +1,6 @@
%Error: t/t_covergroup_sized_run_bad.v:21: Coverage bin array size must be a positive integer (IEEE 1800-2023 19.5.1)
%Error: t/t_covergroup_sized_run_bad.v:21: Coverage bin array 'b' size must be a positive integer (IEEE 1800-2023 19.5.1)
-Info: t/t_covergroup_sized_run_bad.v:21: Verilog $stop, ignored due to +verilator+error+limit
%Error: t/t_covergroup_sized_run_bad.v:21: Coverage bin array size must be a positive integer (IEEE 1800-2023 19.5.1)
%Warning: t/t_covergroup_sized_run_bad.v:27: Coverage bin array needs more bins than --coverage-max-bins; bin ignored
%Error: t/t_covergroup_sized_run_bad.v:21: Coverage bin array 'b' size must be a positive integer (IEEE 1800-2023 19.5.1)
%Warning: t/t_covergroup_sized_run_bad.v:27: Coverage bin array needs more bins than --coverage-max-bins; bin 'b' ignored
%Warning: t/t_covergroup_sized_run_bad.v:45: Unsupported: cross coverage with more than 2^32-1 tuples; cross ignored
*-* All Finished *-*
+16 -46
View File
@@ -9,12 +9,6 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:103:18: Unsupported: function call in coverage select expression
103 | bins one = crossfunc();
| ^~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:117:21: Unsupported: 'with' in cover bin (bin created without filter)
117 | { bins ba = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:120:32: Unsupported: 'with' in wildcard cover bin
120 | { wildcard bins bwaw = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:123:27: Unsupported: 'sequence' in default cover bin
123 | { bins defs = default sequence; }
| ^~~~~~~~
@@ -51,24 +45,6 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:141:12: Unsupported: 'bins' array (non-auto)
141 | { bins nonAuto[4]; }
| ^~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:143:35: Unsupported: 'with' in cover bin (bin created without filter)
143 | { ignore_bins ib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:144:37: Unsupported: 'with' in cover bin (bin created without filter)
144 | { illegal_bins lib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:146:29: Unsupported: 'with' in cover bin
146 | { ignore_bins ib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:147:31: Unsupported: 'with' in cover bin
147 | { illegal_bins lib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:149:45: Unsupported: 'with' in wildcard cover bin
149 | { wildcard ignore_bins wib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:150:47: Unsupported: 'with' in wildcard cover bin
150 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:152:7: Unsupported: 'wildcard' transition list in cover bin
152 | { wildcard ignore_bins wib_trans = ( 1 => 2 ); }
| ^~~~~~~~
@@ -84,35 +60,29 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:158:24: Unsupported: 'bins' explicit array size (treated as '[]')
158 | { bins trans_sized[2] = ( 1 => 2 ); }
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:24: Unsupported: 'with' in cover bin
163 | bins div_by_2 = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:32: Unsupported: 'with' in cover bin
164 | bins div_by_2_paren[] = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:20: Unsupported: 'with' in coverage select expression
170 | bins bin_e = with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:20: Unsupported: 'with' in coverage select expression
163 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:24: Unsupported: 'with' in coverage select expression
171 | bins bin_not_e = ! with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:24: Unsupported: 'with' in coverage select expression
164 | bins bin_not_e = ! with (a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:33: Unsupported: 'with' in coverage select expression
173 | bins bin_with = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:166:33: Unsupported: 'with' in coverage select expression
166 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:49: Unsupported: 'with' in coverage select expression
174 | bins bin_or_with = binsof(a) || binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:167:49: Unsupported: 'with' in coverage select expression
167 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:175:50: Unsupported: 'with' in coverage select expression
175 | bins bin_and_with = binsof(a) && binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:50: Unsupported: 'with' in coverage select expression
168 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:40: Unsupported: 'with' in coverage select expression
177 | ignore_bins ib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:40: Unsupported: 'with' in coverage select expression
170 | ignore_bins ib_cross = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:42: Unsupported: 'with' in coverage select expression
178 | illegal_bins lib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:42: Unsupported: 'with' in coverage select expression
171 | illegal_bins lib_cross = binsof(a) with (a);
| ^~~~
%Error-UNSUPPORTED: t/t_covergroup_unsup.v:203:5: Unsupported: covergroup inheritance (extends)
203 | covergroup extends cg_empty;
%Error-UNSUPPORTED: t/t_covergroup_unsup.v:196:5: Unsupported: covergroup inheritance (extends)
196 | covergroup extends cg_empty;
| ^~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to
-7
View File
@@ -158,13 +158,6 @@ module t (
{ bins trans_sized[2] = ( 1 => 2 ); }
endgroup
covergroup cg_coverpoint_ref;
coverpoint a {
bins div_by_2 = a with (item % 2 == 0);
bins div_by_2_paren[] = a with (item % 2 == 0);
}
endgroup
covergroup cg_cross_bins;
cross a, b {
bins bin_e = with (a);
+62 -64
View File
@@ -9,12 +9,6 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:103:18: Unsupported: function call in coverage select expression
103 | bins one = crossfunc();
| ^~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:117:21: Unsupported: 'with' in cover bin (bin created without filter)
117 | { bins ba = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:120:32: Unsupported: 'with' in wildcard cover bin
120 | { wildcard bins bwaw = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:123:27: Unsupported: 'sequence' in default cover bin
123 | { bins defs = default sequence; }
| ^~~~~~~~
@@ -51,24 +45,6 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:141:12: Unsupported: 'bins' array (non-auto)
141 | { bins nonAuto[4]; }
| ^~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:143:35: Unsupported: 'with' in cover bin (bin created without filter)
143 | { ignore_bins ib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:144:37: Unsupported: 'with' in cover bin (bin created without filter)
144 | { illegal_bins lib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:146:29: Unsupported: 'with' in cover bin
146 | { ignore_bins ib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:147:31: Unsupported: 'with' in cover bin
147 | { illegal_bins lib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:149:45: Unsupported: 'with' in wildcard cover bin
149 | { wildcard ignore_bins wib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:150:47: Unsupported: 'with' in wildcard cover bin
150 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:152:7: Unsupported: 'wildcard' transition list in cover bin
152 | { wildcard ignore_bins wib_trans = ( 1 => 2 ); }
| ^~~~~~~~
@@ -84,32 +60,26 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:158:24: Unsupported: 'bins' explicit array size (treated as '[]')
158 | { bins trans_sized[2] = ( 1 => 2 ); }
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:24: Unsupported: 'with' in cover bin
163 | bins div_by_2 = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:32: Unsupported: 'with' in cover bin
164 | bins div_by_2_paren[] = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:20: Unsupported: 'with' in coverage select expression
170 | bins bin_e = with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:20: Unsupported: 'with' in coverage select expression
163 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:24: Unsupported: 'with' in coverage select expression
171 | bins bin_not_e = ! with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:24: Unsupported: 'with' in coverage select expression
164 | bins bin_not_e = ! with (a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:33: Unsupported: 'with' in coverage select expression
173 | bins bin_with = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:166:33: Unsupported: 'with' in coverage select expression
166 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:49: Unsupported: 'with' in coverage select expression
174 | bins bin_or_with = binsof(a) || binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:167:49: Unsupported: 'with' in coverage select expression
167 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:175:50: Unsupported: 'with' in coverage select expression
175 | bins bin_and_with = binsof(a) && binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:50: Unsupported: 'with' in coverage select expression
168 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:40: Unsupported: 'with' in coverage select expression
177 | ignore_bins ib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:40: Unsupported: 'with' in coverage select expression
170 | ignore_bins ib_cross = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:42: Unsupported: 'with' in coverage select expression
178 | illegal_bins lib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:42: Unsupported: 'with' in coverage select expression
171 | illegal_bins lib_cross = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:98:18: Ignoring unsupported coverage cross option: 'comment'
98 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; }
@@ -120,37 +90,65 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:103:7: Unsupported: explicit coverage cross bins
103 | bins one = crossfunc();
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:7: Unsupported: explicit coverage cross bins
170 | bins bin_e = with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:117:21: Unsupported: 'with' in cover bin outside a coverpoint; bin 'ba' ignored
117 | { bins ba = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:120:32: Unsupported: 'with' in cover bin outside a coverpoint; bin 'bwaw' ignored
120 | { wildcard bins bwaw = {a} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:143:35: Unsupported: 'with' in cover bin outside a coverpoint; bin 'ib_with' ignored
143 | { ignore_bins ib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:144:37: Unsupported: 'with' in cover bin outside a coverpoint; bin 'lib_with' ignored
144 | { illegal_bins lib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:146:29: Unsupported: 'with' in cover bin outside a coverpoint; bin 'ib_cp' ignored
146 | { ignore_bins ib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:147:31: Unsupported: 'with' in cover bin outside a coverpoint; bin 'lib_cp' ignored
147 | { illegal_bins lib_cp = a with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:149:45: Unsupported: 'with' in cover bin outside a coverpoint; bin 'wib_with' ignored
149 | { wildcard ignore_bins wib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:150:47: Unsupported: 'with' in cover bin outside a coverpoint; bin 'wlib_with' ignored
150 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); }
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:7: Unsupported: explicit coverage cross bins
163 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Unsupported: explicit coverage cross bins
171 | bins bin_not_e = ! with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:7: Unsupported: explicit coverage cross bins
164 | bins bin_not_e = ! with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:7: Unsupported: explicit coverage cross bins
173 | bins bin_with = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:166:7: Unsupported: explicit coverage cross bins
166 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:7: Unsupported: explicit coverage cross bins
174 | bins bin_or_with = binsof(a) || binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:167:7: Unsupported: explicit coverage cross bins
167 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:175:7: Unsupported: explicit coverage cross bins
175 | bins bin_and_with = binsof(a) && binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:7: Unsupported: explicit coverage cross bins
168 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:47: Unsupported: 'binsof' in coverage select expression
176 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:47: Unsupported: 'binsof' in coverage select expression
169 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:7: Unsupported: explicit coverage cross bins
176 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:7: Unsupported: explicit coverage cross bins
169 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:7: Unsupported: explicit coverage cross bins
177 | ignore_bins ib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:7: Unsupported: explicit coverage cross bins
170 | ignore_bins ib_cross = binsof(a) with (a);
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Unsupported: explicit coverage cross bins
178 | illegal_bins lib_cross = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Unsupported: explicit coverage cross bins
171 | illegal_bins lib_cross = binsof(a) with (a);
| ^~~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:46:5: Ignoring unsupported: 'type_option.merge_instances'; type coverage is the weighted average of the instances
: ... note: In instance 't'
46 | type_option.merge_instances = 1;
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:47:5: Ignoring unsupported: 'type_option.distribute_first'; 'with' filters apply before values are distributed to bins
: ... note: In instance 't'
47 | type_option.distribute_first = 1;
| ^~~~~~~~~~~
%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:76:5: Logical operator COVERPOINT 'a' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits.
: ... note: In instance 't'
76 | coverpoint a iff (b);
@@ -189,7 +187,7 @@
: ... note: In instance 't'
101 | cross a, b {
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint)
%Warning-COVERIGN: t/t_covergroup_unsup.v:162:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint)
: ... note: In instance 't'
169 | cross a, b {
162 | cross a, b {
| ^
@@ -1,3 +1,7 @@
cg.ct.auto_0: 4
cg.ct.auto_1: 4
cg.ct.auto_2: 4
cg.ct.auto_3: 4
cg.ign.all: 8
cg.ign.odd [ignore]: 8
cg.ign_sized.all: 8
@@ -6,3 +10,6 @@ cg.ill.all: 16
cg.ill.high [illegal]: 0
cg.ill_sized.all: 16
cg.ill_sized.odd [illegal]: 0
cg.mixed.low: 4
cg.with_ign.all: 16
cg.xm.sel_low [cross]: 4
@@ -7,6 +7,7 @@
// 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).
// Bins filtering as many ranges of values with 'with' are ignored, excluding no values.
// verilog_format: off
`define stop $stop
@@ -17,6 +18,7 @@ module t;
bit [3:0] v;
bit [3:0] x;
int illegal;
bit [1:0] t;
covergroup cg;
// The odd values are excluded, so 'all' holds the even ones
@@ -40,6 +42,27 @@ module t;
only: coverpoint v {
wildcard bins odd[2] = {4'b???1};
}
with_only: coverpoint v {
wildcard bins odd[] = {4'b???1} with (1);
}
with_ign: coverpoint v {
bins all = {[0 : 15]};
wildcard ignore_bins odd = {4'b???1} with (item > 1);
}
// A cross selects the bins ignored as no bins, which it then does not have
mixed: coverpoint v {
bins low = {[0 : 3]};
wildcard bins odd[] = {4'b???1} with (item > 8);
wildcard bins odd_sized[2] = {4'b???1};
bins many[] = {[0 : 15]};
}
ct: coverpoint t;
xm: cross mixed, ct{
bins sel_odd = binsof (mixed.odd);
bins sel_sized = binsof (mixed.odd_sized);
bins sel_many = binsof (mixed.many);
bins sel_low = binsof (mixed.low);
}
endgroup
cg cg_inst = new;
@@ -48,9 +71,10 @@ module t;
for (int i = 0; i < 16; ++i) begin
v = 4'(i);
x = 4'(i % 4 * 2);
t = 2'(i);
cg_inst.sample();
end
// All of the coverpoints with bins, which 'only' is not
// All of the coverpoints with bins, which 'only' and 'with_only' are not
`checkr(cg_inst.get_inst_coverage(), 100.0);
if ($value$plusargs("illegal=%d", illegal)) begin
x = 4'(illegal);
@@ -1,3 +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
[0] %Error: t_covergroup_wildcard_max_bins.v:36: Assertion failed in top.t.cg.sample: Illegal bin 'high' hit in coverpoint 'ill'
%Error: t/t_covergroup_wildcard_max_bins.v:36: Verilog $stop
Aborting...
@@ -1,3 +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
[0] %Error: t_covergroup_wildcard_max_bins.v:40: Assertion failed in top.t.cg.sample: Illegal bin 'odd' hit in coverpoint 'ill_sized'
%Error: t/t_covergroup_wildcard_max_bins.v:40: Verilog $stop
Aborting...
+215
View File
@@ -0,0 +1,215 @@
__vlAnonCG_cg.cp.selected[1]: 1
__vlAnonCG_cg.cp.selected[2]: 1
__vlAnonCG_cg.cp.selected[3]: 1
__vlAnonCG_cg.cp.selected[4]: 1
__vlAnonCG_cg_member.cp.b[2]: 3
__vlAnonCG_cg_member.cp.b[3]: 4
cg_bounds.lower.lower[2]: 1
cg_bounds.lower.lower[3]: 1
cg_bounds.lower.lower[4]: 1
cg_bounds.single.single[2]: 1
cg_bounds.upper.upper[0]: 1
cg_bounds.upper.upper[1]: 2
cg_capture.ar.b[0]: 0
cg_capture.ar.b[1]: 0
cg_capture.ar.b[2]: 0
cg_capture.sc.b: 0
cg_cross.aa.even_values[0]: 4
cg_cross.aa.even_values[2]: 4
cg_cross.bb.sized[0]: 8
cg_cross.bb.sized[1]: 8
cg_cross.xx.even_values[0]_x_sized[0] [cross]: 2
cg_cross.xx.sized_one [cross]: 4
cg_cross.xx.two [cross]: 3
cg_default.cp.gated: 1
cg_default.cp.remaining [default]: 6
cg_domain.cp.ignored [ignore]: 1
cg_domain.cp.illegal [illegal]: 0
cg_domain.cp.selected[0]: 1
cg_domain.cp.selected[1]: 1
cg_domain.cp.selected[2]: 1
cg_domain.cp.selected[3]: 1
cg_domain.cp__05b0__05d.excluded [ignore]: 2
cg_domain.cp__05b0__05d.remaining [default]: 9
cg_domain.cp__05b0__05d.selected: 2
cg_domain.wild.ignored [ignore]: 1
cg_domain.wild.ignored_set [ignore]: 0
cg_domain.wild.illegal [illegal]: 0
cg_domain.wild.illegal_set [illegal]: 0
cg_domain.wild.selected[0]: 1
cg_domain.wild.selected[1]: 1
cg_domain.wild.selected[2]: 1
cg_domain.wild.selected[3]: 1
cg_guard.filtered.auto_1: 0
cg_guard.filtered.auto_2: 0
cg_guard.filtered.auto_3: 0
cg_guard.filtered.auto_4: 0
cg_guard.filtered.auto_5: 0
cg_guard.filtered.auto_6: 0
cg_guard.filtered.auto_7: 0
cg_guard.filtered.bad [illegal]: 0
cg_guard.sized.auto_2: 0
cg_guard.sized.auto_3: 0
cg_guard.sized.auto_4: 0
cg_guard.sized.auto_5: 0
cg_guard.sized.auto_6: 0
cg_guard.sized.auto_7: 0
cg_guard.sized.bad[0] [illegal]: 0
cg_guard.sized.bad[1] [illegal]: 0
cg_iff.checked.listed [illegal]: 0
cg_iff.checked.named [illegal]: 0
cg_iff.checked.probe[0]: 2
cg_iff.checked.probe[5]: 1
cg_iff.checked.wild_listed [illegal]: 0
cg_iff.checked.wild_named [illegal]: 0
cg_iff.counted.listed: 1
cg_iff.counted.named[0]: 0
cg_iff.counted.named[1]: 1
cg_iff.counted.wild_listed: 1
cg_iff.counted.wild_named[0]: 0
cg_iff.counted.wild_named[1]: 1
cg_iff.ignored.listed [ignore]: 1
cg_iff.ignored.named [ignore]: 0
cg_iff.ignored.probe[0]: 1
cg_iff.ignored.probe[5]: 1
cg_iff.ignored.wild_listed [ignore]: 0
cg_iff.ignored.wild_named [ignore]: 0
cg_ignore.auto_max.auto_1: 2
cg_ignore.auto_max.auto_2: 1
cg_ignore.auto_max.auto_3: 0
cg_ignore.auto_max.ignored [ignore]: 4
cg_ignore.cp.auto_0: 1
cg_ignore.cp.auto_2: 1
cg_ignore.cp.auto_4: 1
cg_ignore.cp.never [illegal]: 0
cg_ignore.cp.odd [ignore]: 4
cg_ignore.wide.excluded [ignore]: 1
cg_ignore.wide.values: 6
cg_item_arg.listed.b[0]: 1
cg_item_arg.listed.b[2]: 3
cg_item_arg.sized.b[0]: 5
cg_item_arg.sized.b[1]: 6
cg_item_arg.sized.b[2]: 15
cg_item_names.flag.on: 36
cg_item_names.item.b[6]: 7
cg_item_names.item.b[7]: 8
cg_item_names.named.guarded: 7
cg_item_names.named.item: 3
cg_item_names.named.outer: 8
cg_item_names.named.scoped[0]: 1
cg_item_names.named.scoped[1]: 2
cg_item_names.named.scoped[3]: 4
cg_item_names.x.guarded_x_on [cross]: 7
cg_item_names.x.outer_x_on [cross]: 8
cg_item_names.x.scoped[0]_x_on [cross]: 1
cg_item_names.x.scoped[1]_x_on [cross]: 2
cg_item_names.x.scoped[3]_x_on [cross]: 4
cg_item_names.x.sel [cross]: 3
cg_item_ref.cp.b[0]: 1
cg_item_ref.cp.b[1]: 2
cg_item_sample.cp.b[6]: 7
cg_item_sample.cp.b[7]: 8
cg_joined.p._q[5]: 6
cg_joined.p._q[6]: 7
cg_joined.p._q[7]: 8
cg_joined.p_.q[1]: 2
cg_joined.p_.q[2]: 3
cg_joined.p_.q[3]: 4
cg_types.calls.c_code: 4
cg_types.calls.inside_set[100]: 0
cg_types.calls.inside_set[101]: 1
cg_types.calls.inside_set[3]: 1
cg_types.calls.pow2[16]: 0
cg_types.calls.pow2[1]: 1
cg_types.calls.pow2[2]: 1
cg_types.calls.pow2[4]: 1
cg_types.calls.pow2[8]: 1
cg_types.calls.sv_function[1]: 1
cg_types.calls.sv_function[2]: 1
cg_types.ee.chosen[2]: 5
cg_types.ee.chosen[4]: 5
cg_types.ee.named[0]: 8
cg_types.ee.named[2]: 5
cg_types.ee.named[4]: 5
cg_types.ee.named_fixed[0]: 8
cg_types.ee.named_fixed[1]: 5
cg_types.ee.named_single: 13
cg_types.oe.named_fixed[0]: 18
cg_types.oe.named_fixed[1]: 0
cg_types.pp.cast_one[13]: 1
cg_types.pp.cast_one[1]: 1
cg_types.pp.cast_one[5]: 1
cg_types.pp.cast_one[9]: 1
cg_types.pp.high_two: 4
cg_types.pp.lo_one[13]: 1
cg_types.pp.lo_one[1]: 1
cg_types.pp.lo_one[5]: 1
cg_types.pp.lo_one[9]: 1
cg_types.se.named[-1]: 10
cg_types.se.named[-2]: 8
cg_types.se.named[1]: 0
cg_types.se.named[2]: 0
cg_types.se.named_fixed[0]: 8
cg_types.se.named_fixed[1]: 10
cg_types.se.named_fixed[2]: 0
cg_types.ss.negatives[-1]: 3
cg_types.ss.negatives[-2]: 3
cg_types.ss.negatives[-3]: 3
cg_types.truth.all: 18
cg_types.truth.literal_truth: 18
cg_types.truth.real_truth: 18
cg_types.truth.wide_truth: 11
cg_types.wild.selected[12]: 1
cg_types.wild.selected[13]: 1
cg_types.wild.selected[8]: 1
cg_types.ws.values[-1]: 8
cg_types.ws.values[-590295810358705651711]: 1
cg_types.ws.values[3]: 9
cg_types.ww.fixed[0]: 4
cg_types.ww.fixed[1]: 10
cg_types.ww.selected[18446744073709551617]: 4
cg_types.ww.single: 8
cg_values.dupfix.duplicated[0]: 1
cg_values.dupfix.duplicated[1]: 3
cg_values.dupran.duplicate_ranges[0]: 1
cg_values.dupran.duplicate_ranges[1]: 2
cg_values.dupran.duplicate_ranges[2]: 1
cg_values.dupran.duplicate_ranges[3]: 1
cg_values.dupran.duplicate_ranges[4]: 1
cg_values.dupran.duplicate_ranges[5]: 1
cg_values.dupval.duplicate_values[0]: 1
cg_values.dupval.duplicate_values[1]: 2
cg_values.dupval.duplicate_values[2]: 1
cg_values.empty.some: 1
cg_values.evens.evens[0]: 1
cg_values.evens.evens[2]: 1
cg_values.evens.evens[4]: 1
cg_values.evens.evens[6]: 1
cg_values.fixed.fixed[0]: 2
cg_values.fixed.fixed[1]: 2
cg_values.gated.gated: 1
cg_values.joined.joined: 5
cg_values.low.low_values: 4
cg_values.order.ordered[0]: 3
cg_values.order.ordered[1]: 2
cg_values.outer.outer_item: 1
cg_wide.fixed.b[0]: 1
cg_wide.fixed.b[1]: 1
cg_wide.fixed.b[2]: 1
cg_wide.fixed_dup.b[0]: 2
cg_wide.fixed_dup.b[1]: 2
cg_wide.plain.b[0]: 1
cg_wide.plain.b[1]: 1
cg_wide.q_fixed.b[0]: 2
cg_wide.q_fixed.b[1]: 2
cg_wide.q_fixed.b[2]: 0
cg_wide.q_fixed.b[3]: 0
cg_wide.single.b: 3
cg_wide.unit.b[0]: 1
cg_wide.unit.b[1]: 0
cg_wide.unit.b[2]: 0
cg_wide.unit.b[3]: 1
cg_wide.values.b[18446744073709551612]: 0
cg_wide.values.b[18446744073709551614]: 1
cg_wide.values.b[18446744073709551616]: 0
cg_wide.values.b[18446744073709551618]: 0
+15
View File
@@ -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)
+589
View File
@@ -0,0 +1,589 @@
// 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
// Coverpoint bins with 'with' filters (IEEE 1800-2023 19.5.1.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);
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`ifdef verilator
`define no_optimize(v) $c(v)
`else
`define no_optimize(v) (v)
`endif
// verilog_format: on
// A constant 'item', which a filter names through its package
package item_pkg;
localparam bit [2:0] item = 2;
endpackage
module t;
typedef logic signed [6:0] signed_t;
typedef struct packed {
logic [1:0] hi;
logic [1:0] lo;
} packed_t;
typedef enum logic [2:0] {
ZERO = 0,
TWO = 2,
FOUR = 4
} enum_t;
typedef enum logic [2:0] {
C = 4,
A = 0,
B = 2,
D = 6
} order_t;
typedef enum logic signed [2:0] {
MINUS_TWO = -2,
PLUS_ONE = 1,
MINUS_ONE = -1,
PLUS_TWO = 2
} signed_enum_t;
localparam logic [70:0] BASE = 71'h1_0000_0000_0000_0000;
localparam bit [64:0] B65 = 65'h1_0000_0000_0000_0000;
localparam int item = 7;
logic [2:0] value;
logic [1:0] a, b;
bit enabled;
int cutoff;
signed_t signed_value;
packed_t packed_value;
enum_t enum_value;
order_t order_value;
signed_enum_t signed_enum_value;
logic [70:0] wide_value;
logic signed [69:0] wide_signed;
logic [3:0] nibble;
logic [17:0] big;
bit [64:0] w65;
bit [63:0] q64;
function automatic bit even(input int candidate);
return candidate % 2 == 0;
endfunction
// Guards true on their second call only
int with_calls;
int sized_calls;
function automatic bit with_second();
++with_calls;
return with_calls == 2;
endfunction
function automatic bit sized_second();
++sized_calls;
return sized_calls == 2;
endfunction
// Filtered first, then grouped; bins keep the order and duplicates of the values
covergroup cg_values;
// One bin of 0, 1, 2
low: coverpoint value {
bins low_values = {[0 : 7]} with (item < 3);
}
// The range list's 'item' is the parameter; the filter's the candidate
outer: coverpoint value {
bins outer_item = {item} with (item == 7);
}
// All of the coverpoint's values, a bin for each even one
evens: coverpoint value {
bins evens[] = evens with (even(int'(item)));
}
// <0, 2>, <4, 6>
fixed: coverpoint value {
bins fixed[2] = {[0 : 7]} with (item % 2 == 0);
}
// Kept duplicates distribute: <0, 0>, <1, 2>
dupfix: coverpoint value {
bins duplicated[2] = {0, 0, 1, 2, 3, 4} with (item < 3);
}
// A bin per distinct value, in value order
dupval: coverpoint value {
bins duplicate_values[] = {2, 0, 0, 1, 1} with (item < 3);
}
dupran: coverpoint value {
bins duplicate_ranges[] = {[4 : 7], [0 : 3], [2 : 5]} with (item < 6);
}
// Filtered values distribute in list order: <5, 1>, <3, 7>
order: coverpoint value {
bins ordered[2] = {5, 1, 3, 7} with (1);
}
// Runs of values continue over elements
joined: coverpoint value {
bins joined = {[0 : 1], [2 : 3], 4} with (item != 3);
}
// The bin counts only when enabled
gated: coverpoint value {
bins gated = {[0 : 7]} with (item < 3) iff (enabled);
}
// A bin without values is not created
empty: coverpoint value {
bins some = {0};
bins none[] = empty with (item > 7);
bins no_value = {1} with (0);
}
endgroup
// A default bin excludes a filtered bin's values, even when its guard is false
covergroup cg_default;
cp: coverpoint value {
bins gated = {0, 1} with (item < 2) iff (enabled);
bins remaining = default;
}
endgroup
// Each form of guarded bins counts or reports a value only when its guard is true, while
// ignore and illegal bins exclude their values from other bins regardless
covergroup cg_iff;
counted: coverpoint nibble {
bins listed = {[0 : 3]} with (item > 0) iff (enabled);
bins named[] = counted with (item < 2) iff (enabled);
wildcard bins wild_listed = {4'b00??} with (item > 0) iff (enabled);
wildcard bins wild_named[] = counted with (item < 2) iff (enabled);
}
ignored: coverpoint nibble {
ignore_bins listed = {[0 : 3]} with (item == 1) iff (enabled);
ignore_bins named = ignored with (item == 2) iff (enabled);
wildcard ignore_bins wild_listed = {4'b00??} with (item == 3) iff (enabled);
wildcard ignore_bins wild_named = ignored with (item == 4) iff (enabled);
bins probe[] = {[0 : 5]};
}
checked: coverpoint value {
illegal_bins listed = {[0 : 3]} with (item == 1) iff (enabled);
illegal_bins named = checked with (item == 2) iff (enabled);
wildcard illegal_bins wild_listed = {3'b0??} with (item == 3) iff (enabled);
wildcard illegal_bins wild_named = checked with (item == 4) iff (enabled);
bins probe[] = {[0 : 5]};
}
endgroup
// Bins of values over the 64-bit word boundary, and sized arrays of elements in value order
covergroup cg_wide;
single: coverpoint w65 {
bins b = {[B65 - 8 : B65 + 7]} with (item % 2 == 0);
}
values: coverpoint w65 {
bins b[] = {[B65 - 4 : B65 + 3]} with (item % 2 == 0);
}
// <B-8, B-6>, <B-4, B-2>, <B, B+2, B+4, B+6>
fixed: coverpoint w65 {
bins b[3] = {[B65 - 8 : B65 + 7]} with (item % 2 == 0);
}
// A value per bin: <B-2>, <B-1>, <B>, <B+1>
unit: coverpoint w65 {
bins b[4] = {[B65 - 2 : B65 + 1]} with (1);
}
// Elements out of order: <B-2, B-1, B+1>, <B-2, B-1, B+1>
fixed_dup: coverpoint w65 {
bins b[2] = {[B65 - 2 : B65 + 1], [B65 - 2 : B65 + 1]} with (item != B65);
}
// <B-3, B-2>, <B+1, B+2>
plain: coverpoint w65 {
bins b[2] = {[B65 - 3 : B65 - 2], [B65 + 1 : B65 + 2]};
}
// 256 values per bin
q_fixed: coverpoint q64 {
bins b[4] = {[0 : 2047]} with (item % 2 == 0);
}
endgroup
// An illegal bin's guard is evaluated once per sample, as for sized arrays of bins
covergroup cg_guard;
filtered: coverpoint value {
illegal_bins bad = {0} with (1) iff (with_second());
}
sized: coverpoint value {
illegal_bins bad[2] = {0, 1} iff (sized_second());
}
endgroup
// Filtered bins as cross dimensions
covergroup cg_cross;
aa: coverpoint a {
bins even_values[] = aa with (item % 2 == 0);
}
bb: coverpoint b {
bins sized[2] = {[0 : 3], [2 : 3]} with (item != 1);
}
xx: cross aa, bb{
bins two = binsof (aa.even_values) intersect {2};
bins sized_one = binsof (bb.sized) intersect {3};
}
endgroup
// Coverpoints of various types; the candidate has the coverpoint's type
covergroup cg_types;
// -3, -2, -1
ss: coverpoint signed_value {
bins negatives[] = {[-3 : 2]} with (item < 0);
}
// A filter is true for a nonzero value: all values but in 'none', which has none
truth: coverpoint signed_value {
bins all = truth with (1);
bins real_truth = truth with (0.25);
bins literal_truth = truth with (bit'("yes"));
bins wide_truth = truth with (item & 7'sh40);
bins none = truth with (0);
}
// 1, 5, 9, 13, by member and by cast
pp: coverpoint packed_value {
bins lo_one[] = {[0 : 15]} with (item.lo == 1);
bins cast_one[] = {[0 : 15]} with (int'(item) % 4 == 1);
bins high_two = {[0 : 15]} with (item[3: 2] == 2);
}
// Named by value: 2, 4
ee: coverpoint enum_value {
bins chosen[] = {ZERO, TWO, FOUR} with (item != ZERO);
// The coverpoint's name denotes its enumerated values: 0, 2, 4; <0>, <4>
bins named[] = ee with (1);
bins named_fixed[2] = ee with (item != TWO);
bins named_single = ee with (item != TWO);
}
// In value order, whatever the declaration order: <0, 2>, <4, 6>
oe: coverpoint order_value {
bins named_fixed[2] = oe with (1);
}
// In signed value order: -2, -1, 1, 2; <-2>, <-1>, <1, 2>
se: coverpoint signed_enum_value {
bins named[] = se with (1);
bins named_fixed[3] = se with (1);
}
// BASE + 1; BASE and BASE + 2; <BASE + 1, BASE + 2>, <BASE + 3>
ww: coverpoint wide_value {
bins selected[] = {[BASE : BASE + 71'd3]} with ((item & 71'd3) == 1);
bins single = {[BASE : BASE + 71'd3]} with (item[0] == 0);
bins fixed[2] = {[BASE : BASE + 71'd3]} with (item != BASE);
}
// -2**69 + 1, -1, 3
ws: coverpoint wide_signed {
bins values[] = {-70'sd1, 3, -70'sh20_0000_0000_0000_0000 + 1} with (1);
}
// 8, 12, 13
wild: coverpoint nibble {
wildcard bins selected[] = {4'b1?0?} with (item != 9);
}
// Calls: an SV function of a variable, C++ code, and a system function
calls: coverpoint big {
bins sv_function[] = {[0 : 200003]} with (int'(item) < cutoff && int'(item) >= 1);
bins c_code = {[0 : 9]} with (`no_optimize(int'(item)) % 3 == 0);
bins pow2[] = {[1 : 20]} with ($countones(item) == 1);
bins inside_set[] = calls with (item inside {3, [100 : 101]});
}
endgroup
// Automatic bins partition before the filtered exclusions: those of 0, 2, 4
covergroup cg_ignore;
cp: coverpoint value {
ignore_bins odd = {[0 : 7]} with (item % 2 != 0);
illegal_bins never = {[0 : 7]} with (item == 6);
}
// Of auto_bin_max 4, the bins [2:3], [4:5], [6:7] keep values
auto_max: coverpoint value {
option.auto_bin_max = 4;
ignore_bins ignored = {[0 : 1], [5 : 6]} with (1);
}
wide: coverpoint wide_value {
bins values = {[0 : 3]};
ignore_bins excluded = {3} with (1);
}
endgroup
// Constructor arguments in bounds, counts and filters
covergroup cg_bounds(input bit [2:0] low, input bit [2:0] high, input bit [2:0] count);
// <2>, <3, 4>
upper: coverpoint signed_value {
bins upper[count] = {[low : $]} with (item <= signed_t'(high));
}
// 2, 3, 4
lower: coverpoint signed_value {
bins lower[] = {[$ : high]} with (item >= signed_t'(low));
}
single: coverpoint signed_value {
bins single[] = {low} with (1);
}
endgroup
// The coverpoint's name under its guard; a wildcard filter of it has no patterns
covergroup cg_domain;
cp: coverpoint value iff (enabled) {
bins selected[] = cp with (item < 4);
ignore_bins ignored = cp with (item == 4);
illegal_bins illegal = cp with (item == 5);
}
wild: coverpoint value iff (enabled) {
wildcard bins selected[] = wild with (item < 4);
wildcard ignore_bins ignored = wild with (item == 4);
wildcard illegal_bins illegal = wild with (item == 5);
wildcard ignore_bins ignored_set = {3'b11?} with (item == 6);
wildcard illegal_bins illegal_set = {3'b11?} with (item == 7);
}
\cp[0] : coverpoint value {
bins selected = {0, 1} with (item != 0);
ignore_bins excluded = {2};
bins remaining = default;
}
endgroup
// Filters are evaluated when constructed: later changes do not redefine the bins
covergroup cg_capture;
sc: coverpoint nibble {
bins b = {[0 : 15]} with (int'(item) < cutoff);
}
ar: coverpoint nibble {
bins b[] = {[0 : 15]} with (int'(item) < cutoff);
}
endgroup
// Members of an enclosing class, for each instance
class Base;
bit [2:0] value;
int minimum;
int limit;
endclass
class Holder extends Base;
covergroup cg(input int offset);
cp: coverpoint value {
bins selected[] = {[minimum + offset : 7]} with (int'(item) < limit);
}
endgroup
function new(int first, int last);
minimum = first;
limit = last;
cg = new(.offset(1));
endfunction
endclass
// 'item' is the candidate only in a filter, which is its scope (IEEE 1800-2023 7.12); elsewhere
// it is the parameter, 7, as bins and coverpoints named 'item' are no variables (19.5)
covergroup cg_item_names;
// The bin 'item' of 0, 1; 7; 6, counted as 'item' is 7; 0, 1, 3
named: coverpoint value {
bins item = {[0 : 7]} with (item < 2);
bins outer = {item};
bins guarded = {[0 : 7]} with (item == 6) iff (item == 7);
bins scoped[] = {[0 : 3]} with (item != item_pkg::item);
}
// The coverpoint 'item': 6, 7
item: coverpoint value {
bins b[] = item with (item > 5);
}
flag: coverpoint enabled {
bins on = {1};
}
x: cross named, flag{bins sel = binsof (named.item);}
endgroup
// An argument 'item', 3, sets the range list and the array size: 0, 2; <4>, <5>, <6, 7>
covergroup cg_item_arg(input bit [2:0] item);
listed: coverpoint value {
bins b[] = {[0 : item]} with (item % 2 == 0);
}
sized: coverpoint value {
bins b[item] = {[0 : 7]} with (item > 3);
}
endgroup
// Arguments 'item' as the coverpoint expression, whose type the candidate has: 6, 7; 0, 1
covergroup cg_item_sample with function sample (bit [2:0] item);
cp: coverpoint item {
bins b[] = cp with (item > 5);
}
endgroup
covergroup cg_item_ref(ref logic [2:0] item);
cp: coverpoint item {
bins b[] = cp with (item < 2);
}
endgroup
// A member 'item' as the coverpoint expression: 2, 3
class ItemMember;
bit [2:0] item;
covergroup cg_member;
cp: coverpoint item {
bins b[] = cp with (item inside {[2 : 3]});
}
endgroup
function new;
cg_member = new;
endfunction
endclass
// Coverpoints and bins whose names join alike, 'p_' of 'q' and 'p' of '_q': 1, 2, 3; 5, 6, 7
covergroup cg_joined;
p_: coverpoint value {
bins q[] = {[0 : 3]} with (item > 0);
}
p: coverpoint value {
bins _q[] = {[4 : 7]} with (item > 4);
}
endgroup
cg_values values_inst = new;
cg_default default_inst = new;
cg_cross cross_inst = new;
cg_types types_inst;
cg_ignore ignore_inst = new;
cg_bounds bounds_inst = new(2, 4, 2);
cg_domain domain_inst = new;
cg_capture capture_inst;
cg_guard guard_inst = new;
cg_wide wide_inst = new;
cg_iff iff_inst = new;
cg_item_names item_names_inst = new;
cg_item_arg item_arg_inst = new(3);
cg_item_sample item_sample_inst = new;
cg_item_ref item_ref_inst;
cg_joined joined_inst = new;
ItemMember item_member;
Holder low_holder;
Holder high_holder;
initial begin
wide_value = 0;
cutoff = 3;
types_inst = new;
capture_inst = new;
cutoff = 10;
low_holder = new(0, 3);
high_holder = new(2, 5);
low_holder.minimum = 7;
low_holder.limit = 0;
high_holder.minimum = 7;
high_holder.limit = 0;
enabled = 0;
for (int i = 0; i < 8; ++i) begin
value = 3'(i);
values_inst.sample();
default_inst.sample();
domain_inst.sample();
end
enabled = 1;
value = 1;
values_inst.sample();
default_inst.sample();
for (int i = 0; i < 5; ++i) begin
value = 3'(i);
domain_inst.sample();
end
for (int i = 0; i < 6; ++i) begin
value = 3'(i);
ignore_inst.sample();
end
for (int i = 0; i < 4; ++i) begin
for (int j = 0; j < 4; ++j) begin
a = 2'(i);
b = 2'(j);
cross_inst.sample();
end
end
for (int i = 0; i < 16; ++i) begin
signed_value = signed_t'(i % 6 - 3);
packed_value = packed_t'(i);
case (i % 3)
0: enum_value = ZERO;
1: enum_value = TWO;
default: enum_value = FOUR;
endcase
order_value = i % 2 == 0 ? A : B;
signed_enum_value = i % 2 == 0 ? MINUS_TWO : MINUS_ONE;
wide_value = BASE + 71'(i % 4);
wide_signed = i % 2 == 0 ? -70'sd1 : 70'sd3;
nibble = 4'(i);
big = 18'(i);
types_inst.sample();
end
signed_value = 7'sh40;
types_inst.sample();
wide_signed = -70'sh20_0000_0000_0000_0000 + 1;
big = 101;
types_inst.sample();
for (int i = 2; i <= 4; ++i) begin
signed_value = signed_t'(i);
bounds_inst.sample();
end
wide_value = 3;
ignore_inst.sample();
nibble = 5;
capture_inst.sample();
`checkr(capture_inst.get_inst_coverage(), 0.0);
low_holder.value = 1;
high_holder.value = 3;
low_holder.cg.sample();
high_holder.cg.sample();
`checkr(low_holder.cg.get_inst_coverage(), 50.0);
`checkr(high_holder.cg.get_inst_coverage(), 50.0);
low_holder.value = 2;
high_holder.value = 4;
low_holder.cg.sample();
high_holder.cg.sample();
`checkr(low_holder.cg.get_inst_coverage(), 100.0);
`checkr(high_holder.cg.get_inst_coverage(), 100.0);
`checkr(values_inst.get_inst_coverage(), 100.0);
`checkr(default_inst.get_inst_coverage(), 100.0);
`checkr(cross_inst.get_inst_coverage(), 100.0);
`checkr(bounds_inst.get_inst_coverage(), 100.0);
`checkr(item, 7);
w65 = B65 - 6;
q64 = 0;
wide_inst.sample();
w65 = B65 - 2;
q64 = 1000;
wide_inst.sample();
w65 = B65 + 1;
q64 = 0;
wide_inst.sample();
w65 = B65 + 4;
q64 = 1000;
wide_inst.sample();
// The guards were false when evaluated, so no illegal bin was hit
value = 0;
guard_inst.sample();
`checkd(with_calls, 1);
`checkd(sized_calls, 1);
enabled = 0;
for (int i = 0; i < 6; ++i) begin
nibble = 4'(i);
value = 3'(i);
iff_inst.sample();
end
enabled = 1;
nibble = 1;
value = 0;
iff_inst.sample();
// Each value v is sampled v + 1 times, so the counts tell the values of each bin
item_ref_inst = new(value);
item_member = new;
enabled = 1;
for (int i = 0; i < 8; ++i) begin
repeat (i + 1) begin
value = 3'(i);
item_member.item = 3'(i);
item_names_inst.sample();
item_arg_inst.sample();
item_sample_inst.sample(3'(i));
item_ref_inst.sample();
item_member.cg_member.sample();
joined_inst.sample();
end
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
+8
View File
@@ -0,0 +1,8 @@
%Error: t/t_covergroup_with_bad.v:15:18: A bin 'with' filter may name only its own coverpoint 'bb', not 'aa' (IEEE 1800-2023 19.5.1.1)
15 | bins x[] = aa with (item > 1);
| ^~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_covergroup_with_bad.v:19:24: Can't find definition of variable: 'item'
19 | bins y[] = {[0 : item]} with (item > 1);
| ^~~~
%Error: Exiting due to
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()
+24
View File
@@ -0,0 +1,24 @@
// 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
// Invalid names in coverpoint bin 'with' filters (IEEE 1800-2023 19.5.1.1)
module t;
bit [3:0] a, b;
covergroup cg;
aa: coverpoint a;
// Only the coverpoint of the bin may be named
bb: coverpoint b {
bins x[] = aa with (item > 1);
}
// The candidate 'item' is only in the filter, not in the range list
cc: coverpoint b {
bins y[] = {[0 : item]} with (item > 1);
}
endgroup
cg inst = new;
initial $finish;
endmodule
@@ -0,0 +1,3 @@
[0] %Error: t_covergroup_with_illegal_bad.v:22: Assertion failed in top.t.cg.sample: Illegal bin 'bad' hit in coverpoint 'cp'
%Error: t/t_covergroup_with_illegal_bad.v:22: Verilog $stop
Aborting...
+18
View File
@@ -0,0 +1,18 @@
#!/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.compile()
test.execute(fails=True, check_finished=False, expect_filename=test.golden_filename)
test.passes()
@@ -0,0 +1,34 @@
// 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
// An illegal bin of a 'with' filter is hit when its guard, evaluated once, is true
module t;
bit [2:0] value;
int calls;
// True on the first call only
function automatic bit first_call();
++calls;
return calls == 1;
endfunction
covergroup cg;
cp: coverpoint value {
bins valid = {[0 : 3]};
illegal_bins bad = {[4 : 7]} with (item != 5) iff (first_call());
}
endgroup
cg inst = new;
initial begin
value = 4;
inst.sample(); // <--- Bad: illegal
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
@@ -0,0 +1,9 @@
%Warning: t/t_covergroup_with_limits.v:21: Coverage bin 'with' filter has more than 2**32 candidate values; bin 'b' ignored
%Warning: t/t_covergroup_with_limits.v:25: Coverage bin 'with' filter has more than 2**32 candidate values; bin 'b' ignored
%Warning: t/t_covergroup_with_limits.v:33: Coverage bin array needs more bins than --coverage-max-bins; bin 'b' ignored
%Warning: t/t_covergroup_with_limits.v:41: Coverage bin 'with' filter keeps values in more ranges than --coverage-max-bins; bin 'b' ignored
%Warning: t/t_covergroup_with_limits.v:49: Coverage bin 'with' filter keeps values in more ranges than --coverage-max-bins; bin 'b' ignored
%Warning: t/t_covergroup_with_limits.v:52: Coverage bin array needs more bins than --coverage-max-bins; bin 'b' ignored
%Error: t/t_covergroup_with_limits.v:55: Coverage bin array 'b' size must be a positive integer (IEEE 1800-2023 19.5.1)
-Info: t/t_covergroup_with_limits.v:55: Verilog $stop, ignored due to +verilator+error+limit
*-* All Finished *-*
+24
View File
@@ -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')
if test.tsan:
test.skip("ThreadSanitizer not compatible with AddressSanitizer\n")
# Bins of filters ignored when constructed, under AddressSanitizer
test.compile(verilator_flags2=[
'--coverage-max-bins 8', '-CFLAGS -fsanitize=address -LDFLAGS -fsanitize=address'
])
test.execute(all_run_flags=['+verilator+error+limit+100'], expect_filename=test.golden_filename)
test.passes()
+91
View File
@@ -0,0 +1,91 @@
// 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
// Limits of the bins of 'with' filters, which are found when the covergroup is constructed
// 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 [32:0] v33;
bit [7:0] v8;
// Each is evaluated for at most 2**32 candidates, which bounds known when constructed give
covergroup cg_candidates(bit [32:0] high, bit [32:0] low);
all: coverpoint v33 {
bins b = {[0 : high]} with (item < 5); // <--- Bad: 2**33 candidates
bins other = {[5 : 10]};
}
listed: coverpoint v33 {
bins b = {[0 : 33'h7fff_ffff], [low : 33'h1_8000_0000]} with (item == 1); // <--- Bad: 2**32 + 1 candidates
}
endgroup
// Of --coverage-max-bins 8
covergroup cg_kept(int count);
// 9 values, once duplicates are merged
values: coverpoint v8 {
bins b[] = {[0 : 5], [0 : 5], [3 : 8]} with (1); // <--- Bad: bins
}
// 8 values, of 14 before duplicates are merged
merged: coverpoint v8 {
bins b[] = {[0 : 5], [0 : 5], [3 : 7]} with (1);
}
// 11 runs of values
runs: coverpoint v8 {
bins b = {[0 : 40]} with (item % 4 == 0); // <--- Bad: runs
}
// 9 runs of one value
single: coverpoint v8 {
bins b = {1, 1, 1, 1, 1, 1, 1, 1, 1} with (1);
}
// A sized array keeps its duplicates
fixed: coverpoint v8 {
bins b[2] = {1, 1, 1, 1, 1, 1, 1, 1, 1} with (1); // <--- Bad: runs
}
many: coverpoint v8 {
bins b[9] = {[0 : 20]} with (1); // <--- Bad: bins
}
counted: coverpoint v8 {
bins b[count] = {[0 : 3]} with (1); // <--- Bad: count
}
endgroup
cg_candidates candidates_inst;
// The limits apply to the values kept, whatever the order of the candidates
covergroup cg_order;
// 9 values, then a range holding them: one range of values
listed: coverpoint v8 {
bins b = {0, 2, 4, 6, 8, 10, 12, 14, 16, [0 : 16]} with (1);
}
// 8 values, each listed twice
twice: coverpoint v8 {
bins b[] = {[0 : 15], [0 : 15]} with (item % 2 == 0);
}
endgroup
cg_kept kept_inst;
cg_order order_inst;
initial begin
candidates_inst = new(33'h1_ffff_ffff, 33'h1_0000_0000);
kept_inst = new(0);
order_inst = new;
v33 = 5;
v8 = 1;
candidates_inst.sample();
kept_inst.sample();
`checkr(candidates_inst.get_inst_coverage(), 100.0);
`checkr(kept_inst.get_inst_coverage(), (100.0 / 8.0 + 100.0) / 2.0);
v8 = 16;
order_inst.sample();
`checkr(order_inst.get_inst_coverage(), 50.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
+23
View File
@@ -0,0 +1,23 @@
#!/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('linter')
test.top_filename = 't/t_covergroup_with.v'
test.lint(verilator_flags2=['-Wwarn-UNUSEDSIGNAL', '-Wwarn-VARHIDDEN', '-Wno-fatal'])
# A 'with' filter may read all, some, or none of the bits of its implicit candidate 'item'
test.file_grep_not(test.compile_log_filename, r"_item'")
# The candidate hides another 'item' in the filter without a warning, unlike the declarations
# the test names 'item', of the members and arguments of classes and covergroups
test.file_grep_count(test.compile_log_filename, r"hides declaration in upper scope: 'item'", 4)
test.passes()
+19
View File
@@ -0,0 +1,19 @@
#!/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_all')
test.top_filename = 't/t_covergroup_with.v'
test.compile()
test.execute()
test.passes()
@@ -0,0 +1,14 @@
%Error: t/t_covergroup_with_type_bad.v:16:26: Bin 'with' filters are not allowed on a coverpoint of a non-integral expression (IEEE 1800-2023 19.5.1.1)
: ... note: In instance 't'
16 | bins b = {[0 : 3]} with (item > 1);
| ^~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_covergroup_with_type_bad.v:19:22: Bin 'with' filters are not allowed on a coverpoint of a non-integral expression (IEEE 1800-2023 19.5.1.1)
: ... note: In instance 't'
19 | bins b = {"a"} with (1);
| ^~~~
%Error: t/t_covergroup_with_type_bad.v:23:34: Bin 'with' filter must be assignment compatible with an integral type, not 'string' (IEEE 1800-2023 19.5.1.1)
: ... note: In instance 't'
23 | bins b[] = {[0 : 3]} with (s);
| ^
%Error: Exiting due to
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()
@@ -0,0 +1,28 @@
// 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
// Invalid types of coverpoint bin 'with' filters (IEEE 1800-2023 19.5.1.1)
module t;
real r;
bit [3:0] a;
string s = "x";
covergroup cg;
// Not allowed for a real coverpoint, nor other non-integral ones
rr: coverpoint r {
bins b = {[0 : 3]} with (item > 1);
}
ss: coverpoint s {
bins b = {"a"} with (1);
}
// The result must be assignment compatible with an integral type
aa: coverpoint a {
bins b[] = {[0 : 3]} with (s);
}
endgroup
cg inst = new;
initial $finish;
endmodule
@@ -0,0 +1,34 @@
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:33:5: Ignoring unsupported: 'type_option.distribute_first'; 'with' filters apply before values are distributed to bins
: ... note: In instance 't'
33 | type_option.distribute_first = 1;
| ^~~~~~~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:17:21: Unsupported: 'with' filter of wildcard 'bins' of more than --coverage-max-bins of 8 ranges of values; bin 'odd' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
17 | wildcard bins odd[] = {8'b???????1} with (1);
| ^~~
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:21:28: Unsupported: 'with' filter of wildcard 'ignore_bins' of more than --coverage-max-bins of 8 ranges of values; bin 'odd' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
21 | wildcard ignore_bins odd = {8'b???????1} with (item > 1);
| ^~~
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:26:21: Unsupported: 'with' filter of wildcard 'bins' of more than --coverage-max-bins of 8 ranges of values; bin 'odd' ignored
: ... note: In instance 't'
: ... Suggest a larger --coverage-max-bins
26 | wildcard bins odd[] = {8'b???????1} with (item > 1);
| ^~~
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:47:12: Unsupported: 'with' filter of more than 2**32 candidate values; bin 'b' ignored
: ... note: In instance 't'
47 | bins b = all with (item < 5);
| ^
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:50:12: Unsupported: 'with' filter of more than 2**32 candidate values; bin 'b' ignored
: ... note: In instance 't'
50 | bins b = {[0 : 33'h1_0000_0000]} with (item < 5);
| ^
%Warning-COVERIGN: t/t_covergroup_with_unsup.v:53:12: Unsupported: 'with' filter of more than 2**32 candidate values; bin 'fixed' ignored
: ... note: In instance 't'
53 | bins fixed[2] = {[0 : 33'h0_ffff_ffff], [0 : 33'h0_ffff_ffff]} with (item < 5);
| ^~~~~
%Error: Exiting due to
+18
View File
@@ -0,0 +1,18 @@
#!/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('linter')
test.lint(verilator_flags2=['--coverage-max-bins 8'],
fails=True,
expect_filename=test.golden_filename)
test.passes()
+62
View File
@@ -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
// Unsupported 'with' filters: of wildcard patterns of more ranges of values than
// --coverage-max-bins 8, or distributed first
module t;
bit [7:0] v;
bit [1:0] t;
bit [32:0] v33;
covergroup cg;
// The odd values are 128 ranges; the coverpoint is left without bins
normal: coverpoint v {
wildcard bins odd[] = {8'b???????1} with (1);
}
excluded: coverpoint v {
bins low = {[0 : 3]};
wildcard ignore_bins odd = {8'b???????1} with (item > 1);
}
// A cross selects the bins ignored as no bins
mixed: coverpoint v {
bins low = {[0 : 3]};
wildcard bins odd[] = {8'b???????1} with (item > 1);
}
ct: coverpoint t;
x: cross mixed, ct{bins sel = binsof (mixed.odd);}
endgroup
// Filters apply before values are distributed (IEEE 1800-2023 19.5.1.1)
covergroup cg_first;
type_option.distribute_first = 1; // <--- Unsupported
cp: coverpoint v {
bins f[3] = {[0 : 5]} with (item < 3);
}
endgroup
covergroup cg_default;
type_option.distribute_first = 0;
cp: coverpoint v {
bins f[3] = {[0 : 5]} with (item < 3);
}
endgroup
// Each is evaluated for at most 2**32 candidates, known when verilated
covergroup cg_candidates;
all: coverpoint v33 {
bins b = all with (item < 5); // <--- Unsupported: 2**33 candidates
}
listed: coverpoint v33 {
bins b = {[0 : 33'h1_0000_0000]} with (item < 5); // <--- Unsupported: 2**32 + 1
// 2**32, as the values are evaluated once
bins merged = {[0 : 33'h0_ffff_ffff], [0 : 33'h0_ffff_ffff]} with (item < 5);
bins fixed[2] = {[0 : 33'h0_ffff_ffff], [0 : 33'h0_ffff_ffff]} with (item < 5); // <--- Unsupported: 2**33
bins at_limit = {[0 : 33'h0_ffff_ffff]} with (item < 5);
}
endgroup
cg inst = new;
cg_first first_inst = new;
cg_candidates candidates_inst = new;
cg_default default_inst = new;
initial $finish;
endmodule
+7
View File
@@ -802,6 +802,13 @@ module Vt_debug_emitv_t;
bins multi = {'sh0, 'sh1, 'sh2};
bins sized['sh2] = {['sh0:'sh5], 'sh7};
wildcard bins wild[] = {3'b1z};
bins filtered[] = {['sh0:'sh7]} with ((
(item
%
'sh2)
==
'sh0));
bins named = cp_sig with ((item > 'sh5));
bins dflt = default;
ignore_bins ign = {'sh7};
illegal_bins ill = {'sh5};
+2
View File
@@ -401,6 +401,8 @@ module t (/*AUTOARG*/
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 filtered[] = {[0:7]} with (item % 2 == 0); // 'with' filter of a range list
bins named = cp_sig with (item > 5); // 'with' filter of the coverpoint's values
bins dflt = default;
ignore_bins ign = {7};
illegal_bins ill = {5};