From bde1b98ee0402a2c250207331a66a84e7d4d0163 Mon Sep 17 00:00:00 2001 From: Marco Bartoli Date: Tue, 29 Sep 2026 00:29:54 +0200 Subject: [PATCH] Support coverpoint bin 'with' filters (#8518) --- include/verilated_covergroup.cpp | 288 ++++++++- include/verilated_covergroup.h | 26 + src/V3AstAttr.h | 9 + src/V3AstNodeOther.h | 14 + src/V3Covergroup.cpp | 570 +++++++++++++---- src/V3EmitV.cpp | 12 + src/V3LinkDot.cpp | 24 + src/V3LinkParse.cpp | 42 ++ src/V3Width.cpp | 42 ++ src/verilog.y | 33 +- test_regress/t/t_covergroup_autobins_bad.out | 8 +- test_regress/t/t_covergroup_bins_dup_bad.out | 10 + test_regress/t/t_covergroup_bins_dup_bad.py | 16 + test_regress/t/t_covergroup_bins_dup_bad.v | 27 + .../t/t_covergroup_exclusions_unsup.out | 4 +- .../t/t_covergroup_max_bins_unsup.out | 8 +- test_regress/t/t_covergroup_sized_bad.out | 12 +- test_regress/t/t_covergroup_sized_bad.v | 3 +- test_regress/t/t_covergroup_sized_run_bad.out | 6 +- test_regress/t/t_covergroup_unsup.out | 62 +- test_regress/t/t_covergroup_unsup.v | 7 - test_regress/t/t_covergroup_unsup_ign2.out | 126 ++-- .../t/t_covergroup_wildcard_max_bins.out | 7 + .../t/t_covergroup_wildcard_max_bins.v | 26 +- ...t_covergroup_wildcard_max_bins_illegal.out | 4 +- ...rgroup_wildcard_max_bins_illegal_sized.out | 4 +- test_regress/t/t_covergroup_with.out | 215 +++++++ test_regress/t/t_covergroup_with.py | 15 + test_regress/t/t_covergroup_with.v | 589 ++++++++++++++++++ test_regress/t/t_covergroup_with_bad.out | 8 + test_regress/t/t_covergroup_with_bad.py | 16 + test_regress/t/t_covergroup_with_bad.v | 24 + .../t/t_covergroup_with_illegal_bad.out | 3 + .../t/t_covergroup_with_illegal_bad.py | 18 + .../t/t_covergroup_with_illegal_bad.v | 34 + test_regress/t/t_covergroup_with_limits.out | 9 + test_regress/t/t_covergroup_with_limits.py | 24 + test_regress/t/t_covergroup_with_limits.v | 91 +++ test_regress/t/t_covergroup_with_lint.py | 23 + .../t/t_covergroup_with_no_coverage.py | 19 + test_regress/t/t_covergroup_with_type_bad.out | 14 + test_regress/t/t_covergroup_with_type_bad.py | 16 + test_regress/t/t_covergroup_with_type_bad.v | 28 + test_regress/t/t_covergroup_with_unsup.out | 34 + test_regress/t/t_covergroup_with_unsup.py | 18 + test_regress/t/t_covergroup_with_unsup.v | 62 ++ test_regress/t/t_debug_emitv.out | 7 + test_regress/t/t_debug_emitv.v | 2 + 48 files changed, 2366 insertions(+), 293 deletions(-) create mode 100644 test_regress/t/t_covergroup_bins_dup_bad.out create mode 100755 test_regress/t/t_covergroup_bins_dup_bad.py create mode 100644 test_regress/t/t_covergroup_bins_dup_bad.v create mode 100644 test_regress/t/t_covergroup_with.out create mode 100755 test_regress/t/t_covergroup_with.py create mode 100644 test_regress/t/t_covergroup_with.v create mode 100644 test_regress/t/t_covergroup_with_bad.out create mode 100755 test_regress/t/t_covergroup_with_bad.py create mode 100644 test_regress/t/t_covergroup_with_bad.v create mode 100644 test_regress/t/t_covergroup_with_illegal_bad.out create mode 100755 test_regress/t/t_covergroup_with_illegal_bad.py create mode 100644 test_regress/t/t_covergroup_with_illegal_bad.v create mode 100644 test_regress/t/t_covergroup_with_limits.out create mode 100755 test_regress/t/t_covergroup_with_limits.py create mode 100644 test_regress/t/t_covergroup_with_limits.v create mode 100755 test_regress/t/t_covergroup_with_lint.py create mode 100755 test_regress/t/t_covergroup_with_no_coverage.py create mode 100644 test_regress/t/t_covergroup_with_type_bad.out create mode 100755 test_regress/t/t_covergroup_with_type_bad.py create mode 100644 test_regress/t/t_covergroup_with_type_bad.v create mode 100644 test_regress/t/t_covergroup_with_unsup.out create mode 100755 test_regress/t/t_covergroup_with_unsup.py create mode 100644 test_regress/t/t_covergroup_with_unsup.v diff --git a/include/verilated_covergroup.cpp b/include/verilated_covergroup.cpp index acce0613a..b223d809a 100644 --- a/include/verilated_covergroup.cpp +++ b/include/verilated_covergroup.cpp @@ -26,6 +26,7 @@ #include "verilated.h" #include +#include #include // This file is compiled whenever covergroups are used, with or without @@ -121,7 +122,21 @@ struct VlCoverpoint::ValueData final { std::vector m_elements; // In declaration order std::vector 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 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 m_reported; // Declared bins that have values, in declaration order std::vector m_sized; // Sized arrays, in sizedFinish() order std::vector 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(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(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 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& 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 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 low; + VlWide 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& 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 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(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(bin[0])), + array.m_first + static_cast(std::min(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; } diff --git a/include/verilated_covergroup.h b/include/verilated_covergroup.h index 71b879173..639b4fb13 100644 --- a/include/verilated_covergroup.h +++ b/include/verilated_covergroup.h @@ -51,6 +51,14 @@ enum class VlCovBinNaming : uint8_t { Single, // "" one bin Array, // "[i]" bins b[N] value array Numbered, // "_" automatic bins of a coverpoint without bins + Values, // "[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. diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index 5c299aa46..44e8b2dc1 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -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, "") \ diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 31263b331..52e69a9e4 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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 diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index da431905f..44e4bd805 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -30,6 +30,7 @@ #include "V3Error.h" #include "V3File.h" #include "V3MemberMap.h" +#include "V3UniqueNames.h" #include #include @@ -295,6 +296,11 @@ class FunctionalCoverageVisitor final : public VNVisitor { std::deque runs; // Runs 'values' refers to }; std::map m_cpBins; // Runtime coverpoint -> binsof index ranges + // Names of the bins declarations each coverpoint ignored, which binsof selects as no bins + std::map> 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 m_detachedValues; // Array-bin values m_cpBins refers to std::set 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> consumers; std::map> 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 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> 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> found{{range.lo, range.hi}}; + if (range.wildcard) { // checkConstructedBins bounded the runs + found.clear(); + crossRangeRuns(range, v3Global.opt.coverageMaxBins(), found); + } + for (const std::pair& 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> merged; + for (const std::pair& 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& 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> 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> coverpointValues(AstNode* nodep, + AstNodeExpr* exprp) { + const int width = runWidth(exprp); + std::vector> 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 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& 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(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::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(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::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 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(fl->lineno()))); + args.push_back(cnum(fl, static_cast(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(fl->lineno())), - cnum(fl, static_cast(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(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> 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 namerStmts; std::vector defaultBins; std::vector 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(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(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 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(values.size()), static_cast(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()); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index b2442668a..fdf4a2c33 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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()); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index e411390be..8ef4bda56 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -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); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 04dc537a6..837649f83 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -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); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 1588d8bea..084d3c342 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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); diff --git a/src/verilog.y b/src/verilog.y index 2dfe570b2..6acbeeb39 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7186,26 +7186,17 @@ bins_or_options: // ==IEEE: bins_or_options | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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{$2, *$2, $6, false, false}; - BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); - DEL($10, $12); $$ = binp; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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{$2, *$2, $6, true, false}; - BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); - DEL($10, $12); $$ = binp; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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{$2, *$2, $6, false, true}; - BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); - DEL($10, $12); $$ = binp; } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$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($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$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($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$2, *$2, new AstCoverWith{$6, new AstCoverpointRef{$5, *$5}, $8}, false, true, false, $10}, $3); } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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: // ==IEEE: bins_or_options | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$3, *$3, $7, false, true, true, $9}, $4); } | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE - { $$ = nullptr; GRAMMARP->coverBinArrayUnsized($4); BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } + { $$ = GRAMMARP->coverBinArray(new AstCoverBin{$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{$3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$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{$3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$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{$3, *$3, new AstCoverWith{$7, new AstCoverpointRef{$6, *$6}, $9}, false, true, true, $11}, $4); } // // // cgexpr part of trans_list | yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE diff --git a/test_regress/t/t_covergroup_autobins_bad.out b/test_regress/t/t_covergroup_autobins_bad.out index 4567c77cc..c4c26ba72 100644 --- a/test_regress/t/t_covergroup_autobins_bad.out +++ b/test_regress/t/t_covergroup_autobins_bad.out @@ -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]}; diff --git a/test_regress/t/t_covergroup_bins_dup_bad.out b/test_regress/t/t_covergroup_bins_dup_bad.out new file mode 100644 index 000000000..b5e69b4d2 --- /dev/null +++ b/test_regress/t/t_covergroup_bins_dup_bad.out @@ -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 diff --git a/test_regress/t/t_covergroup_bins_dup_bad.py b/test_regress/t/t_covergroup_bins_dup_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_covergroup_bins_dup_bad.py @@ -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() diff --git a/test_regress/t/t_covergroup_bins_dup_bad.v b/test_regress/t/t_covergroup_bins_dup_bad.v new file mode 100644 index 000000000..77ea2d22c --- /dev/null +++ b/test_regress/t/t_covergroup_bins_dup_bad.v @@ -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 diff --git a/test_regress/t/t_covergroup_exclusions_unsup.out b/test_regress/t/t_covergroup_exclusions_unsup.out index 7cef59f7e..32bf352b5 100644 --- a/test_regress/t/t_covergroup_exclusions_unsup.out +++ b/test_regress/t/t_covergroup_exclusions_unsup.out @@ -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}; diff --git a/test_regress/t/t_covergroup_max_bins_unsup.out b/test_regress/t/t_covergroup_max_bins_unsup.out index a3fdfc59a..6649bf3a1 100644 --- a/test_regress/t/t_covergroup_max_bins_unsup.out +++ b/test_regress/t/t_covergroup_max_bins_unsup.out @@ -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????_????}; diff --git a/test_regress/t/t_covergroup_sized_bad.out b/test_regress/t/t_covergroup_sized_bad.out index 285b45065..bb81003bd 100644 --- a/test_regress/t/t_covergroup_sized_bad.out +++ b/test_regress/t/t_covergroup_sized_bad.out @@ -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 diff --git a/test_regress/t/t_covergroup_sized_bad.v b/test_regress/t/t_covergroup_sized_bad.v index 9543bc4c6..c7a2fafc3 100644 --- a/test_regress/t/t_covergroup_sized_bad.v +++ b/test_regress/t/t_covergroup_sized_bad.v @@ -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: '$' } diff --git a/test_regress/t/t_covergroup_sized_run_bad.out b/test_regress/t/t_covergroup_sized_run_bad.out index c45939083..58df6a787 100644 --- a/test_regress/t/t_covergroup_sized_run_bad.out +++ b/test_regress/t/t_covergroup_sized_run_bad.out @@ -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 *-* diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 0da03208d..73877b475 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -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 diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index 6d79f6cf3..82f7d98c8 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -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); diff --git a/test_regress/t/t_covergroup_unsup_ign2.out b/test_regress/t/t_covergroup_unsup_ign2.out index 339cce618..7ad58069c 100644 --- a/test_regress/t/t_covergroup_unsup_ign2.out +++ b/test_regress/t/t_covergroup_unsup_ign2.out @@ -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 { | ^ diff --git a/test_regress/t/t_covergroup_wildcard_max_bins.out b/test_regress/t/t_covergroup_wildcard_max_bins.out index 8ccb297c2..f8447c497 100644 --- a/test_regress/t/t_covergroup_wildcard_max_bins.out +++ b/test_regress/t/t_covergroup_wildcard_max_bins.out @@ -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 diff --git a/test_regress/t/t_covergroup_wildcard_max_bins.v b/test_regress/t/t_covergroup_wildcard_max_bins.v index f02652e91..dcece1fa2 100644 --- a/test_regress/t/t_covergroup_wildcard_max_bins.v +++ b/test_regress/t/t_covergroup_wildcard_max_bins.v @@ -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); diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out index 894db71b2..be92004cc 100644 --- a/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal.out @@ -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... diff --git a/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out index d9d6a45e3..e2d9cf6b4 100644 --- a/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out +++ b/test_regress/t/t_covergroup_wildcard_max_bins_illegal_sized.out @@ -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... diff --git a/test_regress/t/t_covergroup_with.out b/test_regress/t/t_covergroup_with.out new file mode 100644 index 000000000..452895c63 --- /dev/null +++ b/test_regress/t/t_covergroup_with.out @@ -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 diff --git a/test_regress/t/t_covergroup_with.py b/test_regress/t/t_covergroup_with.py new file mode 100755 index 000000000..9f6b5465d --- /dev/null +++ b/test_regress/t/t_covergroup_with.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_with.v b/test_regress/t/t_covergroup_with.v new file mode 100644 index 000000000..c12e130ad --- /dev/null +++ b/test_regress/t/t_covergroup_with.v @@ -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); + } + // , , + fixed: coverpoint w65 { + bins b[3] = {[B65 - 8 : B65 + 7]} with (item % 2 == 0); + } + // A value per bin: , , , + unit: coverpoint w65 { + bins b[4] = {[B65 - 2 : B65 + 1]} with (1); + } + // Elements out of order: , + fixed_dup: coverpoint w65 { + bins b[2] = {[B65 - 2 : B65 + 1], [B65 - 2 : B65 + 1]} with (item != B65); + } + // , + 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; , + 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 diff --git a/test_regress/t/t_covergroup_with_bad.out b/test_regress/t/t_covergroup_with_bad.out new file mode 100644 index 000000000..5ea3622bf --- /dev/null +++ b/test_regress/t/t_covergroup_with_bad.out @@ -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 diff --git a/test_regress/t/t_covergroup_with_bad.py b/test_regress/t/t_covergroup_with_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_covergroup_with_bad.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_bad.v b/test_regress/t/t_covergroup_with_bad.v new file mode 100644 index 000000000..eeb4352fc --- /dev/null +++ b/test_regress/t/t_covergroup_with_bad.v @@ -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 diff --git a/test_regress/t/t_covergroup_with_illegal_bad.out b/test_regress/t/t_covergroup_with_illegal_bad.out new file mode 100644 index 000000000..ecc38c179 --- /dev/null +++ b/test_regress/t/t_covergroup_with_illegal_bad.out @@ -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... diff --git a/test_regress/t/t_covergroup_with_illegal_bad.py b/test_regress/t/t_covergroup_with_illegal_bad.py new file mode 100755 index 000000000..c402f9e45 --- /dev/null +++ b/test_regress/t/t_covergroup_with_illegal_bad.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_illegal_bad.v b/test_regress/t/t_covergroup_with_illegal_bad.v new file mode 100644 index 000000000..651c49347 --- /dev/null +++ b/test_regress/t/t_covergroup_with_illegal_bad.v @@ -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 diff --git a/test_regress/t/t_covergroup_with_limits.out b/test_regress/t/t_covergroup_with_limits.out new file mode 100644 index 000000000..d54fbaace --- /dev/null +++ b/test_regress/t/t_covergroup_with_limits.out @@ -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 *-* diff --git a/test_regress/t/t_covergroup_with_limits.py b/test_regress/t/t_covergroup_with_limits.py new file mode 100755 index 000000000..159ca81e3 --- /dev/null +++ b/test_regress/t/t_covergroup_with_limits.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +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() diff --git a/test_regress/t/t_covergroup_with_limits.v b/test_regress/t/t_covergroup_with_limits.v new file mode 100644 index 000000000..155b73497 --- /dev/null +++ b/test_regress/t/t_covergroup_with_limits.v @@ -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 diff --git a/test_regress/t/t_covergroup_with_lint.py b/test_regress/t/t_covergroup_with_lint.py new file mode 100755 index 000000000..364100fc9 --- /dev/null +++ b/test_regress/t/t_covergroup_with_lint.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_no_coverage.py b/test_regress/t/t_covergroup_with_no_coverage.py new file mode 100755 index 000000000..e164ac1e1 --- /dev/null +++ b/test_regress/t/t_covergroup_with_no_coverage.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_type_bad.out b/test_regress/t/t_covergroup_with_type_bad.out new file mode 100644 index 000000000..18c7897db --- /dev/null +++ b/test_regress/t/t_covergroup_with_type_bad.out @@ -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 diff --git a/test_regress/t/t_covergroup_with_type_bad.py b/test_regress/t/t_covergroup_with_type_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_covergroup_with_type_bad.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_type_bad.v b/test_regress/t/t_covergroup_with_type_bad.v new file mode 100644 index 000000000..828b1ac57 --- /dev/null +++ b/test_regress/t/t_covergroup_with_type_bad.v @@ -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 diff --git a/test_regress/t/t_covergroup_with_unsup.out b/test_regress/t/t_covergroup_with_unsup.out new file mode 100644 index 000000000..9e5440df4 --- /dev/null +++ b/test_regress/t/t_covergroup_with_unsup.out @@ -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 diff --git a/test_regress/t/t_covergroup_with_unsup.py b/test_regress/t/t_covergroup_with_unsup.py new file mode 100755 index 000000000..61fede5f5 --- /dev/null +++ b/test_regress/t/t_covergroup_with_unsup.py @@ -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() diff --git a/test_regress/t/t_covergroup_with_unsup.v b/test_regress/t/t_covergroup_with_unsup.v new file mode 100644 index 000000000..da0c64938 --- /dev/null +++ b/test_regress/t/t_covergroup_with_unsup.v @@ -0,0 +1,62 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// 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 diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 3371bbab6..dd46c9d89 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -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}; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index d46bf4129..c8e85368b 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -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};