mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424)
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
|
||||
// This file is compiled whenever covergroups are used, with or without
|
||||
// "verilator --coverage" (see V3Global::verilatedCppFiles). Bin counts are
|
||||
// owned by the covergroup instance nodes in the VerilatedContext's registry, so
|
||||
@@ -38,6 +41,491 @@
|
||||
#include "verilated_cov.h"
|
||||
#endif
|
||||
|
||||
struct VlCoverpoint::ValueData final {
|
||||
// CONSTANTS
|
||||
static constexpr uint32_t QUERY_WORK_LIMIT = 1U << 20; // Maximum graph steps per query
|
||||
static constexpr uint32_t QUERY_DEPTH_LIMIT = 1024; // Max width for recursive queries
|
||||
|
||||
// TYPES
|
||||
// A value's words, held inline up to INLINE_WORDS so that common widths do not allocate
|
||||
class Value final {
|
||||
static constexpr uint32_t INLINE_WORDS = 2; // Words stored without an allocation
|
||||
uint32_t m_size = 0; // Number of words
|
||||
EData m_inline[INLINE_WORDS] = {0, 0}; // Words of a value up to INLINE_WORDS wide
|
||||
std::vector<EData> m_heap; // Words of a wider value
|
||||
|
||||
public:
|
||||
Value() = default;
|
||||
Value(const EData* beginp, const EData* endp)
|
||||
: m_size{static_cast<uint32_t>(endp - beginp)} {
|
||||
if (m_size <= INLINE_WORDS) {
|
||||
std::copy(beginp, endp, m_inline);
|
||||
} else {
|
||||
m_heap.assign(beginp, endp);
|
||||
}
|
||||
}
|
||||
EData* data() { return m_size <= INLINE_WORDS ? m_inline : m_heap.data(); }
|
||||
const EData* data() const { return m_size <= INLINE_WORDS ? m_inline : m_heap.data(); }
|
||||
bool empty() const { return !m_size; }
|
||||
void clear() {
|
||||
m_size = 0;
|
||||
m_heap.clear();
|
||||
}
|
||||
EData& operator[](uint32_t i) { return data()[i]; }
|
||||
const EData& operator[](uint32_t i) const { return data()[i]; }
|
||||
EData& back() { return data()[m_size - 1]; }
|
||||
EData* begin() { return data(); }
|
||||
EData* end() { return data() + m_size; }
|
||||
const EData* begin() const { return data(); }
|
||||
const EData* end() const { return data() + m_size; }
|
||||
bool operator==(const Value& other) const {
|
||||
return std::equal(begin(), end(), other.begin(), other.end());
|
||||
}
|
||||
};
|
||||
struct Range final {
|
||||
Value m_lo; // Inclusive lower bound and fixed-bit values for wildcard patterns
|
||||
Value m_hi; // Inclusive upper bound in coverpoint value order
|
||||
Value m_mask; // Significant wildcard bits; empty for an ordinary interval
|
||||
};
|
||||
struct Values final {
|
||||
std::vector<Range> m_ranges; // Source intervals and patterns associated with this bin
|
||||
bool m_transition = false; // State-value exclusions must not alter this transition bin
|
||||
};
|
||||
// Outcome of searching a range for a value outside every exclusion
|
||||
enum class Search : uint8_t { EMPTY, VALUE, WORK_LIMIT, DEPTH_LIMIT };
|
||||
class Query;
|
||||
|
||||
// MEMBERS
|
||||
const uint32_t m_bits; // Width of the coverpoint's effective integral type
|
||||
const uint32_t m_words; // EData words required to store one value
|
||||
const bool m_isSigned; // Use signed ordering when comparing values
|
||||
bool m_frozen = false; // Construction-time value metadata has been finalized
|
||||
std::vector<Values> m_values; // Values by declared bin, released once crosses are built
|
||||
std::vector<Range> m_exclusions; // Normalized state ignore/illegal ranges and patterns
|
||||
uint32_t m_regularExclusions = 0; // Length of the merged interval prefix in m_exclusions
|
||||
std::vector<uint32_t> m_reported; // Declared bins that have values, in declaration order
|
||||
|
||||
ValueData(uint32_t bits, bool isSigned, uint32_t bins)
|
||||
: m_bits{bits}
|
||||
, m_words{VL_WORDS_I(bits)}
|
||||
, m_isSigned{isSigned}
|
||||
, m_values{bins} {
|
||||
assert(m_bits);
|
||||
}
|
||||
static WDataInP view(const Value& value) { return WDataInP::external(value.data()); }
|
||||
Value read(WDataInP valuep) const {
|
||||
Value result(valuep.datap(), valuep.datap() + m_words);
|
||||
return result;
|
||||
}
|
||||
// Operands have already been cleaned to m_bits.
|
||||
bool less(WDataInP lhs, WDataInP rhs) const {
|
||||
if (m_isSigned) {
|
||||
const EData leftSign = VL_SIGN_E(m_bits, lhs[m_words - 1]);
|
||||
const EData rightSign = VL_SIGN_E(m_bits, rhs[m_words - 1]);
|
||||
if (leftSign != rightSign) return leftSign;
|
||||
}
|
||||
for (uint32_t i = m_words; i > 0; --i) {
|
||||
const EData left = lhs[i - 1];
|
||||
const EData right = rhs[i - 1];
|
||||
if (left != right) return left < right;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool less(const Value& lhs, const Value& rhs) const { return less(view(lhs), view(rhs)); }
|
||||
bool less(WDataInP lhs, const Value& rhs) const { return less(lhs, view(rhs)); }
|
||||
bool less(const Value& lhs, WDataInP rhs) const { return less(view(lhs), rhs); }
|
||||
void increment(Value& value) const {
|
||||
for (EData& word : value) {
|
||||
if (++word) break;
|
||||
}
|
||||
value.back() &= VL_MASK_E(m_bits);
|
||||
}
|
||||
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()) {
|
||||
EData mismatch = 0;
|
||||
for (uint32_t i = 0; i < m_words; ++i) {
|
||||
mismatch |= (value[i] & range.m_mask[i]) ^ (range.m_lo[i] & range.m_mask[i]);
|
||||
}
|
||||
return mismatch == 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const Range* interval(const std::vector<Range>& ranges, uint32_t count, WDataInP value) const {
|
||||
auto it = std::upper_bound(
|
||||
ranges.begin(), ranges.begin() + count, value,
|
||||
[&](WDataInP candidate, const Range& range) { return less(candidate, range.m_lo); });
|
||||
if (it == ranges.begin()) return nullptr;
|
||||
--it;
|
||||
return contains(*it, value) ? &*it : nullptr;
|
||||
}
|
||||
uint32_t normalize(std::vector<Range>& ranges) const {
|
||||
// Most bins have one ordered range, which needs no sorting or merging.
|
||||
if (ranges.size() == 1 && !less(ranges[0].m_hi, ranges[0].m_lo)) {
|
||||
return ranges[0].m_mask.empty() ? 1 : 0;
|
||||
}
|
||||
const auto middle = std::stable_partition(
|
||||
ranges.begin(), ranges.end(), [](const Range& range) { return range.m_mask.empty(); });
|
||||
std::sort(ranges.begin(), middle,
|
||||
[&](const Range& lhs, const Range& rhs) { return less(lhs.m_lo, rhs.m_lo); });
|
||||
std::vector<Range> merged;
|
||||
for (auto it = ranges.begin(); it != middle; ++it) {
|
||||
if (less(it->m_hi, it->m_lo)) continue;
|
||||
if (!merged.empty()) {
|
||||
Value adjacent = merged.back().m_hi;
|
||||
increment(adjacent);
|
||||
if (!less(merged.back().m_hi, it->m_lo) || adjacent == it->m_lo) {
|
||||
if (less(merged.back().m_hi, it->m_hi)) merged.back().m_hi = it->m_hi;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
merged.push_back(std::move(*it));
|
||||
}
|
||||
const uint32_t count = static_cast<uint32_t>(merged.size());
|
||||
merged.insert(merged.end(), std::make_move_iterator(middle),
|
||||
std::make_move_iterator(ranges.end()));
|
||||
ranges = std::move(merged);
|
||||
return count;
|
||||
}
|
||||
bool excluded(WDataInP value) const {
|
||||
return interval(m_exclusions, m_regularExclusions, value)
|
||||
|| std::any_of(m_exclusions.begin() + m_regularExclusions, m_exclusions.end(),
|
||||
[&](const Range& range) { return contains(range, value); });
|
||||
}
|
||||
// Value bits, unlike wildcard mask bits, flip the sign bit for signed ordering.
|
||||
bool orderBit(const Value& value, uint32_t bit) const {
|
||||
return (VL_BITISSET_W(value, bit) != 0) ^ (m_isSigned && bit == m_bits - 1);
|
||||
}
|
||||
void setOrderBit(Value& value, uint32_t bit, bool ordered) const {
|
||||
VL_ASSIGNBIT_II(bit, value[VL_BITWORD_E(bit)],
|
||||
ordered ^ (m_isSigned && bit == m_bits - 1));
|
||||
}
|
||||
bool patternAtLeast(const Range& range, const Value& lower, Value& result) const {
|
||||
if (range.m_mask.empty()) {
|
||||
result = lower;
|
||||
return true;
|
||||
}
|
||||
result = range.m_lo;
|
||||
int32_t carry = -1;
|
||||
bool greater = false;
|
||||
for (uint32_t pos = m_bits; pos > 0;) {
|
||||
const uint32_t bit = --pos;
|
||||
const bool fixed = VL_BITISSET_W(range.m_mask, bit);
|
||||
const bool low = orderBit(lower, bit);
|
||||
const bool chosen = fixed ? orderBit(range.m_lo, bit) : greater ? false : low;
|
||||
if (!greater && fixed && !chosen && low) {
|
||||
if (carry < 0) return false;
|
||||
setOrderBit(result, static_cast<uint32_t>(carry), true);
|
||||
for (uint32_t tail = 0; tail < static_cast<uint32_t>(carry); ++tail) {
|
||||
const uint32_t w = VL_BITWORD_E(tail);
|
||||
VL_ASSIGNBIT_II(tail, result[w],
|
||||
VL_BITISSET_E(range.m_lo[w] & range.m_mask[w], tail) != 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!greater && !fixed && !chosen) carry = static_cast<int32_t>(bit);
|
||||
if (chosen != low) greater |= chosen && !low;
|
||||
setOrderBit(result, bit, chosen);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool clip(Range& range, const Value& lo, const Value& hi) const {
|
||||
const Value lower = less(lo, range.m_lo) ? range.m_lo : lo;
|
||||
const Value upper = less(range.m_hi, hi) ? range.m_hi : hi;
|
||||
Value first;
|
||||
if (less(upper, lower) || !patternAtLeast(range, lower, first) || less(upper, first)) {
|
||||
return false;
|
||||
}
|
||||
range.m_lo = std::move(first);
|
||||
range.m_hi = upper;
|
||||
return true;
|
||||
}
|
||||
bool pattern(WDataInP valuep, WDataInP maskp, WDataInP lop, WDataInP hip,
|
||||
Range& result) const {
|
||||
result = {read(valuep), read(valuep), read(maskp)};
|
||||
for (uint32_t i = 0; i < m_words; ++i) {
|
||||
result.m_lo[i] &= result.m_mask[i];
|
||||
result.m_hi[i] |= ~result.m_mask[i];
|
||||
}
|
||||
result.m_hi.back() &= VL_MASK_E(m_bits);
|
||||
if (m_isSigned && !VL_SIGN_E(m_bits, result.m_mask.back())) {
|
||||
VL_ASSIGNBIT_IO(m_bits - 1, result.m_lo.back());
|
||||
VL_ASSIGNBIT_II(m_bits - 1, result.m_hi.back(), 0);
|
||||
}
|
||||
bool fixed = false;
|
||||
bool contiguous = true;
|
||||
for (uint32_t bit = 0; bit < m_bits; ++bit) {
|
||||
if (VL_BITISSET_W(result.m_mask, bit)) {
|
||||
fixed = true;
|
||||
} else if (fixed) {
|
||||
contiguous = false;
|
||||
}
|
||||
}
|
||||
if (contiguous) result.m_mask.clear();
|
||||
return clip(result, read(lop), read(hip));
|
||||
}
|
||||
Value lastValue(const Range& range) const {
|
||||
Range reversed = range;
|
||||
Value lower = range.m_hi;
|
||||
for (uint32_t word = 0; word < m_words; ++word) {
|
||||
reversed.m_lo[word] = ~reversed.m_lo[word];
|
||||
lower[word] = ~lower[word];
|
||||
}
|
||||
reversed.m_lo.back() &= VL_MASK_E(m_bits);
|
||||
lower.back() &= VL_MASK_E(m_bits);
|
||||
Value result;
|
||||
const bool found VL_ATTR_UNUSED = patternAtLeast(reversed, lower, result);
|
||||
assert(found);
|
||||
for (EData& word : result) word = ~word;
|
||||
result.back() &= VL_MASK_E(m_bits);
|
||||
return result;
|
||||
}
|
||||
Search hasValue(uint32_t bin, const Range& range) const;
|
||||
Search intersects(uint32_t bin, const Range& filter) const;
|
||||
};
|
||||
|
||||
// One bounded search. Shared ordered decisions avoid expanding the complement of wildcard
|
||||
// exclusions; the graph is discarded with the query, so instances retain none of it.
|
||||
class VlCoverpoint::ValueData::Query final {
|
||||
struct Decision final {
|
||||
uint32_t m_position; // One-based value-bit position; zero denotes a terminal
|
||||
uint32_t m_low; // Child node ID for a zero-valued ordering bit
|
||||
uint32_t m_high; // Child node ID for a one-valued ordering bit
|
||||
uint32_t m_inverse; // Complement node ID, or UINT32_MAX if not cached
|
||||
};
|
||||
|
||||
const ValueData& m_data; // Value width and ordering of the queried coverpoint
|
||||
std::vector<Decision> m_decisions{{0, 0, 0, 1}, {0, 1, 1, 0}}; // Nodes; 0=false, 1=true
|
||||
// (Position, low, high) -> canonical node ID
|
||||
std::map<std::tuple<uint32_t, uint32_t, uint32_t>, uint32_t> m_unique;
|
||||
std::map<std::pair<uint32_t, uint32_t>, uint32_t> m_combined; // Cached intersection roots
|
||||
uint32_t m_work = 0; // Graph steps consumed by this query
|
||||
bool m_limited = false; // The work limit was exceeded, so the result is unknown
|
||||
|
||||
bool step() {
|
||||
if (m_limited) return false;
|
||||
if (++m_work <= QUERY_WORK_LIMIT) return true;
|
||||
m_limited = true;
|
||||
return false;
|
||||
}
|
||||
uint32_t decision(uint32_t position, uint32_t low, uint32_t high) {
|
||||
if (m_limited) return 0;
|
||||
if (low == high) return low;
|
||||
const auto key = std::make_tuple(position, low, high);
|
||||
const auto it = m_unique.find(key);
|
||||
if (it != m_unique.end()) return it->second;
|
||||
const uint32_t result = static_cast<uint32_t>(m_decisions.size());
|
||||
m_decisions.push_back({position, low, high, UINT32_MAX});
|
||||
m_unique.emplace(key, result);
|
||||
return result;
|
||||
}
|
||||
uint32_t rangeDecision(const Range& range, uint32_t position, uint32_t bounds,
|
||||
std::vector<std::array<uint32_t, 4>>& cache) {
|
||||
if (!step()) return 0;
|
||||
uint32_t& cached = cache[position][bounds];
|
||||
if (cached != UINT32_MAX) return cached;
|
||||
const uint32_t bit = position - 1;
|
||||
const uint32_t lower = m_data.orderBit(range.m_lo, bit);
|
||||
const uint32_t upper = m_data.orderBit(range.m_hi, bit);
|
||||
uint32_t children[2] = {0, 0};
|
||||
for (uint32_t value = 0; value < 2; ++value) {
|
||||
if ((!range.m_mask.empty() && VL_BITISSET_W(range.m_mask, bit) && value != lower)
|
||||
|| ((bounds & 1U) && value < lower) || ((bounds & 2U) && value > upper)) {
|
||||
continue;
|
||||
}
|
||||
const uint32_t next = ((bounds & 1U) && value == lower ? 1U : 0U)
|
||||
| ((bounds & 2U) && value == upper ? 2U : 0U);
|
||||
children[value] = rangeDecision(range, position - 1, next, cache);
|
||||
}
|
||||
cached = decision(position, children[0], children[1]);
|
||||
return cached;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Query(const ValueData& data)
|
||||
: m_data{data} {}
|
||||
bool limited() const { return m_limited; }
|
||||
uint32_t intersect(uint32_t lhs, uint32_t rhs) {
|
||||
if (!step()) return 0;
|
||||
if (lhs == rhs) return lhs;
|
||||
if (!lhs || !rhs) return 0;
|
||||
if (lhs == 1) return rhs;
|
||||
if (rhs == 1) return lhs;
|
||||
if (rhs < lhs) std::swap(lhs, rhs);
|
||||
const auto key = std::make_pair(lhs, rhs);
|
||||
const auto it = m_combined.find(key);
|
||||
if (it != m_combined.end()) return it->second;
|
||||
// Recursive calls can grow decisions, so do not retain references into it.
|
||||
const Decision left = m_decisions[lhs];
|
||||
const Decision right = m_decisions[rhs];
|
||||
const uint32_t position = std::max(left.m_position, right.m_position);
|
||||
const uint32_t low = intersect(left.m_position == position ? left.m_low : lhs,
|
||||
right.m_position == position ? right.m_low : rhs);
|
||||
const uint32_t high = intersect(left.m_position == position ? left.m_high : lhs,
|
||||
right.m_position == position ? right.m_high : rhs);
|
||||
const uint32_t result = decision(position, low, high);
|
||||
m_combined.emplace(key, result);
|
||||
return result;
|
||||
}
|
||||
uint32_t negate(uint32_t root) {
|
||||
if (!step()) return 0;
|
||||
if (m_decisions[root].m_inverse != UINT32_MAX) return m_decisions[root].m_inverse;
|
||||
const Decision node = m_decisions[root];
|
||||
const uint32_t low = negate(node.m_low);
|
||||
const uint32_t high = negate(node.m_high);
|
||||
const uint32_t result = decision(node.m_position, low, high);
|
||||
m_decisions[root].m_inverse = result;
|
||||
m_decisions[result].m_inverse = root;
|
||||
return result;
|
||||
}
|
||||
uint32_t rangeRoot(const Range& range) {
|
||||
if (m_data.less(range.m_hi, range.m_lo)) return 0;
|
||||
std::vector<std::array<uint32_t, 4>> cache(m_data.m_bits + 1);
|
||||
for (auto& entry : cache) entry.fill(UINT32_MAX);
|
||||
cache[0].fill(1);
|
||||
return rangeDecision(range, m_data.m_bits, 3, cache);
|
||||
}
|
||||
};
|
||||
|
||||
VlCoverpoint::ValueData::Search VlCoverpoint::ValueData::hasValue(uint32_t bin,
|
||||
const Range& range) const {
|
||||
if (m_values[bin].m_transition || m_exclusions.empty() || !excluded(view(range.m_lo))) {
|
||||
return Search::VALUE;
|
||||
}
|
||||
const Value last = lastValue(range);
|
||||
if (!excluded(view(last))) return Search::VALUE;
|
||||
if (last == range.m_lo) return Search::EMPTY;
|
||||
// Try cheap witnesses first; only difficult queries need a bounded symbolic search.
|
||||
if (m_bits > QUERY_DEPTH_LIMIT) return Search::DEPTH_LIMIT;
|
||||
Query query{*this};
|
||||
uint32_t root = query.rangeRoot(range);
|
||||
for (const Range& exclusion : m_exclusions) {
|
||||
root = query.intersect(root, query.negate(query.rangeRoot(exclusion)));
|
||||
if (!root) break;
|
||||
}
|
||||
if (query.limited()) return Search::WORK_LIMIT;
|
||||
return root ? Search::VALUE : Search::EMPTY;
|
||||
}
|
||||
|
||||
VlCoverpoint::ValueData::Search VlCoverpoint::ValueData::intersects(uint32_t bin,
|
||||
const Range& filter) const {
|
||||
Search result = Search::EMPTY;
|
||||
for (const Range& source : m_values[bin].m_ranges) {
|
||||
Range range = source;
|
||||
if (!clip(range, filter.m_lo, filter.m_hi)) continue;
|
||||
const Search search = hasValue(bin, range);
|
||||
if (search == Search::VALUE) return search;
|
||||
if (search != Search::EMPTY) result = search;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VlCoverpoint::VlCoverpoint() = default;
|
||||
VlCoverpoint::~VlCoverpoint() = default;
|
||||
|
||||
void VlCoverpoint::valueType(uint32_t bits, bool isSigned) {
|
||||
assert(!m_valuesp);
|
||||
m_valuesp.reset(new ValueData{bits, isSigned, m_total});
|
||||
}
|
||||
|
||||
void VlCoverpoint::valueRanges(std::initializer_list<EData> entries) {
|
||||
ValueData& data = *m_valuesp;
|
||||
assert(!data.m_frozen);
|
||||
const uint32_t words = data.m_words;
|
||||
assert(entries.size() % (1 + 2 * words) == 0);
|
||||
for (const EData* entryp = entries.begin(); entryp != entries.end(); entryp += 1 + 2 * words) {
|
||||
data.m_values[entryp[0]].m_ranges.push_back(
|
||||
{data.read(WDataInP::external(entryp + 1)),
|
||||
data.read(WDataInP::external(entryp + 1 + words)),
|
||||
{}});
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverpoint::valuePatterns(std::initializer_list<EData> entries) {
|
||||
ValueData& data = *m_valuesp;
|
||||
assert(!data.m_frozen);
|
||||
const uint32_t words = data.m_words;
|
||||
assert(entries.size() % (1 + 4 * words) == 0);
|
||||
for (const EData* entryp = entries.begin(); entryp != entries.end(); entryp += 1 + 4 * words) {
|
||||
ValueData::Range range;
|
||||
if (data.pattern(WDataInP::external(entryp + 1), WDataInP::external(entryp + 1 + words),
|
||||
WDataInP::external(entryp + 1 + 2 * words),
|
||||
WDataInP::external(entryp + 1 + 3 * words), range)) {
|
||||
data.m_values[entryp[0]].m_ranges.push_back(std::move(range));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverpoint::valueTransitions(std::initializer_list<uint32_t> bins) {
|
||||
for (const uint32_t bin : bins) m_valuesp->m_values[bin].m_transition = true;
|
||||
}
|
||||
|
||||
bool VlCoverpoint::liveBin(uint32_t bin) const {
|
||||
const ValueData& data = *m_valuesp;
|
||||
ValueData::Search limit = ValueData::Search::EMPTY;
|
||||
for (const ValueData::Range& range : data.m_values[bin].m_ranges) {
|
||||
const ValueData::Search search = data.hasValue(bin, range);
|
||||
if (search == ValueData::Search::VALUE) return true;
|
||||
if (search != ValueData::Search::EMPTY) limit = search;
|
||||
}
|
||||
if (limit == ValueData::Search::EMPTY) return false;
|
||||
// Keep a bin whose exclusions cannot be analyzed, rather than stop the simulation.
|
||||
const VlCovNamer& namer = namerFor(bin);
|
||||
VL_WARN_MT(
|
||||
namer.file(), namer.line(), "",
|
||||
limit == ValueData::Search::WORK_LIMIT
|
||||
? "Coverage bin exclusions exceed the decision-graph work limit; bin retained"
|
||||
: "Coverage bin exclusions exceed the decision-graph depth limit; bin retained");
|
||||
return true;
|
||||
}
|
||||
|
||||
void VlCoverpoint::valueFinalize() {
|
||||
ValueData& data = *m_valuesp;
|
||||
assert(!data.m_frozen);
|
||||
for (const VlCovNamer& namer : m_namers) {
|
||||
const bool exclusion = namer.set() == VlCovBinKind::KIND_IGNORE
|
||||
|| namer.set() == VlCovBinKind::KIND_ILLEGAL;
|
||||
for (uint32_t bin = namer.base(); bin < namer.base() + namer.count(); ++bin) {
|
||||
ValueData::Values& values = data.m_values[bin];
|
||||
data.normalize(values.m_ranges);
|
||||
if (exclusion && !values.m_transition) {
|
||||
data.m_exclusions.insert(data.m_exclusions.end(), values.m_ranges.begin(),
|
||||
values.m_ranges.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
data.m_regularExclusions = data.normalize(data.m_exclusions);
|
||||
m_crossToBin.clear();
|
||||
std::fill(m_crossIdx.begin(), m_crossIdx.end(), -1);
|
||||
m_normal = 0;
|
||||
// Normal bins without values leave the report and the coverage denominator.
|
||||
for (const VlCovNamer& namer : m_namers) {
|
||||
const bool normal = namer.set() == VlCovBinKind::KIND_NORMAL;
|
||||
for (uint32_t bin = namer.base(); bin < namer.base() + namer.count(); ++bin) {
|
||||
if (normal && !liveBin(bin)) continue;
|
||||
data.m_reported.push_back(bin);
|
||||
if (!normal) continue;
|
||||
m_crossIdx[bin] = static_cast<int>(m_normal++);
|
||||
m_crossToBin.push_back(bin);
|
||||
}
|
||||
}
|
||||
data.m_frozen = true;
|
||||
}
|
||||
|
||||
void VlCoverpoint::valueRelease() {
|
||||
ValueData& data = *m_valuesp;
|
||||
assert(data.m_frozen);
|
||||
std::vector<ValueData::Values>{}.swap(data.m_values);
|
||||
}
|
||||
|
||||
bool VlCoverpoint::valueExcluded(QData value) const {
|
||||
VlWide<VL_WQ_WORDS_E> words;
|
||||
VL_SET_WQ(words, value);
|
||||
return valueExcludedW(words);
|
||||
}
|
||||
|
||||
bool VlCoverpoint::valueExcludedW(WDataInP valuep) const { return m_valuesp->excluded(valuep); }
|
||||
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, uint32_t nBins) {
|
||||
m_hier = hier;
|
||||
m_atLeast = atLeast;
|
||||
@@ -63,7 +551,7 @@ void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming nam
|
||||
|
||||
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
|
||||
// Build the bin name based on the bin index
|
||||
return binName(m_crossToBin[crossIdx]);
|
||||
return declaredBinName(m_crossToBin[crossIdx]);
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
|
||||
@@ -75,19 +563,30 @@ const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
|
||||
return *std::prev(it);
|
||||
}
|
||||
|
||||
std::string VlCoverpoint::binName(uint32_t i) const {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
std::string VlCoverpoint::declaredBinName(uint32_t bin) const {
|
||||
const VlCovNamer& nm = namerFor(bin);
|
||||
std::string name = nm.name();
|
||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(bin - nm.base()) + ']';
|
||||
return name;
|
||||
}
|
||||
|
||||
uint32_t VlCoverpoint::reportedBin(uint32_t i) const {
|
||||
return m_valuesp ? m_valuesp->m_reported[i] : i;
|
||||
}
|
||||
|
||||
uint32_t VlCoverpoint::binCount() const {
|
||||
return m_valuesp ? static_cast<uint32_t>(m_valuesp->m_reported.size()) : m_total;
|
||||
}
|
||||
|
||||
std::string VlCoverpoint::binName(uint32_t i) const { return declaredBinName(reportedBin(i)); }
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
for (uint32_t i = 0; i < binCount(); ++i) {
|
||||
for (uint32_t reported = 0; reported < binCount(); ++reported) {
|
||||
const uint32_t i = reportedBin(reported);
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
const VlCovBinKind kind = binKind(i);
|
||||
const std::string binp = binName(i);
|
||||
const VlCovBinKind kind = binKind(reported);
|
||||
const std::string binp = binName(reported);
|
||||
const std::string full = m_hier + "." + binp;
|
||||
const std::string lineStr = std::to_string(nm.line());
|
||||
const std::string colStr = std::to_string(nm.col());
|
||||
@@ -137,12 +636,18 @@ void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cp
|
||||
void VlCoverCross::addBin(VlCovBinKind kind, std::initializer_list<uint64_t> selection,
|
||||
const char* namep, const char* filep, int line, int col) {
|
||||
if (!m_numAutoBins) return; // An empty product creates no cross bin.
|
||||
addBinImpl(kind, selection.begin(), static_cast<uint32_t>(selection.size()), namep, filep,
|
||||
line, col, m_explicitp->numBins);
|
||||
}
|
||||
|
||||
void VlCoverCross::addBinImpl(VlCovBinKind kind, const uint64_t* sourcep, uint32_t words,
|
||||
const char* namep, const char* filep, int line, int col,
|
||||
uint32_t iffIndex) {
|
||||
Explicit& data = *m_explicitp;
|
||||
const uint32_t words = m_numAutoBins / 64 + (m_numAutoBins % 64 != 0);
|
||||
assert(selection.size() == words);
|
||||
assert(words == VL_BITWORD_Q(static_cast<uint64_t>(m_numAutoBins) + VL_QUADSIZE - 1));
|
||||
assert(data.numBins < data.bins.size());
|
||||
uint64_t* const selectionp = data.selectionp + static_cast<uint64_t>(data.numBins) * words;
|
||||
std::copy(selection.begin(), selection.end(), selectionp);
|
||||
std::copy(sourcep, sourcep + words, selectionp);
|
||||
Bin& bin = data.bins[data.numBins++];
|
||||
bin.selectionp = selectionp;
|
||||
bin.namep = namep;
|
||||
@@ -150,9 +655,11 @@ void VlCoverCross::addBin(VlCovBinKind kind, std::initializer_list<uint64_t> sel
|
||||
bin.line = line;
|
||||
bin.col = col;
|
||||
bin.kind = kind;
|
||||
bin.iffIndex = iffIndex;
|
||||
if (kind == VlCovBinKind::KIND_NORMAL) ++data.normalBins;
|
||||
uint32_t word = 0;
|
||||
for (const uint64_t bits : selection) { data.wordsp[word++].autoExcluded |= bits; }
|
||||
for (uint32_t word = 0; word < words; ++word) {
|
||||
data.wordsp[word].autoExcluded |= selectionp[word];
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCross::finalizeBins() {
|
||||
@@ -226,7 +733,7 @@ void VlCoverCross::sampleSingleTuple(uint32_t idx, const bool* binIffs) {
|
||||
return;
|
||||
}
|
||||
for (Bin& bin : data.bins) {
|
||||
if (T_ApplyIffs && !*binIffs++) continue;
|
||||
if (T_ApplyIffs && !binIffs[bin.iffIndex]) continue;
|
||||
if (bin.selectionp[word] & bit) incrementBin(bin);
|
||||
}
|
||||
}
|
||||
@@ -247,8 +754,8 @@ void VlCoverCross::sampleBins(const bool* binIffs) {
|
||||
cached[i] = {word, wordsp[word].hitBits};
|
||||
}
|
||||
for (uint64_t binIdx = 0; binIdx < bins; ++binIdx) {
|
||||
if (T_ApplyIffs && !*binIffs++) continue;
|
||||
Bin& bin = data.bins[binIdx];
|
||||
if (T_ApplyIffs && !binIffs[bin.iffIndex]) continue;
|
||||
bool matched = false;
|
||||
if (T_Touched == 1) {
|
||||
matched = (bin.selectionp[cached[0].index] & cached[0].bits) != 0;
|
||||
@@ -289,6 +796,7 @@ void VlCoverCross::sampleHitWords(const bool* binIffs) {
|
||||
}
|
||||
|
||||
void VlCoverCross::sample(const bool* binIffs) {
|
||||
if (VL_UNLIKELY(!m_numAutoBins)) return;
|
||||
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
|
||||
bool single = true;
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
@@ -313,9 +821,9 @@ void VlCoverCross::sample(const bool* binIffs) {
|
||||
return;
|
||||
}
|
||||
bool enabled = true;
|
||||
if (hasExplicitBins() && binIffs && !binIffs[0]) {
|
||||
const bool* const endp = binIffs + m_explicitp->bins.size();
|
||||
enabled = std::find(binIffs + 1, endp, true) != endp;
|
||||
if (hasExplicitBins() && binIffs && !binIffs[m_explicitp->bins[0].iffIndex]) {
|
||||
enabled = std::any_of(m_explicitp->bins.begin() + 1, m_explicitp->bins.end(),
|
||||
[binIffs](const Bin& bin) { return binIffs[bin.iffIndex]; });
|
||||
if (!enabled && m_explicitp->autoBins.empty()) return;
|
||||
}
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
@@ -412,6 +920,247 @@ void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCrossDyn
|
||||
|
||||
class VlCoverCrossDyn::Layout final {
|
||||
friend class VlCoverCrossDyn;
|
||||
|
||||
using Mask = std::vector<uint64_t>;
|
||||
using Search = VlCoverpoint::ValueData::Search;
|
||||
struct Selected final {
|
||||
Bin m_info{}; // Declaration metadata, bin kind, and original iff index
|
||||
Mask m_mask; // Tuple-selection bitmap for the declared bin
|
||||
};
|
||||
uint32_t m_tuples = 0; // Cartesian product of live coverpoint-bin counts
|
||||
uint32_t m_words = 0; // 64-bit words per tuple-selection bitmap
|
||||
std::vector<Dimension> m_dimensions; // Coverpoint bindings, hit lists, and tuple strides
|
||||
std::vector<uint32_t> m_counts; // Dense automatic-bin counters, one slot per flat tuple ID
|
||||
std::vector<Bin> m_bins; // Final compacted explicit-bin records
|
||||
std::vector<Word> m_hitWords; // Auto-exclusion and hit masks, plus touched-word IDs
|
||||
std::vector<uint32_t> m_autoBins; // Flat tuple IDs retained as automatic cross bins
|
||||
std::vector<uint32_t> m_binWords; // Packed nonzero selection-word indices per explicit bin
|
||||
std::vector<uint64_t> m_selections; // Contiguous bitmaps for finalized explicit bins
|
||||
Explicit m_explicitData{{nullptr, 0},
|
||||
nullptr,
|
||||
{nullptr, 0},
|
||||
{nullptr, 0},
|
||||
nullptr}; // Storage views bound to the base sampler
|
||||
std::vector<Mask> m_stack; // Postfix evaluation stack for construction-time selections
|
||||
std::vector<Selected> m_selected; // Declared bins pending exclusion and compaction
|
||||
uint32_t m_selectDimension = 0; // Dimension whose binsof selection is being built
|
||||
uint32_t m_selectFirst = 0; // First declared coverpoint bin named by the binsof term
|
||||
uint32_t m_selectEnd = 0; // One past the last declared coverpoint bin named by binsof
|
||||
bool m_negate = false; // Complement the completed dimension membership mask
|
||||
Search m_limit = Search::EMPTY; // A search limit left the current bin's selection unknown
|
||||
std::vector<bool> m_allowed; // Normal-bin membership mask for the current dimension
|
||||
|
||||
static void setRange(Mask& mask, uint64_t first, uint64_t end) {
|
||||
while (first < end) {
|
||||
const uint64_t bit = VL_BITBIT_Q(first);
|
||||
const uint64_t bits = std::min<uint64_t>(VL_QUADSIZE - bit, end - first);
|
||||
mask[VL_BITWORD_Q(first)]
|
||||
|= (bits == VL_QUADSIZE ? ~uint64_t{0} : (uint64_t{1} << bits) - 1) << bit;
|
||||
first += bits;
|
||||
}
|
||||
}
|
||||
bool named(uint32_t index) const {
|
||||
const uint32_t bin = m_dimensions[m_selectDimension].cpp->m_crossToBin[index];
|
||||
return bin >= m_selectFirst && bin < m_selectEnd;
|
||||
}
|
||||
void range(WDataInP lop, WDataInP hip) {
|
||||
const VlCoverpoint* const cpp = m_dimensions[m_selectDimension].cpp;
|
||||
const VlCoverpoint::ValueData& data = *cpp->m_valuesp;
|
||||
const VlCoverpoint::ValueData::Range filter{data.read(lop), data.read(hip), {}};
|
||||
for (uint32_t i = 0; i < m_allowed.size(); ++i) {
|
||||
if (m_allowed[i] || !named(i)) continue;
|
||||
const Search search = data.intersects(cpp->m_crossToBin[i], filter);
|
||||
if (search == Search::VALUE) {
|
||||
m_allowed[i] = true;
|
||||
} else if (search != Search::EMPTY) {
|
||||
m_limit = search;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VlCoverCrossDyn::VlCoverCrossDyn()
|
||||
: VlCoverCross{0, 0}
|
||||
, m_layoutp{new Layout} {}
|
||||
|
||||
VlCoverCrossDyn::~VlCoverCrossDyn() = default;
|
||||
|
||||
void VlCoverCrossDyn::init(const char* hier, uint32_t dims, VlCoverpoint* const* cps,
|
||||
const char* file, int line, int col) {
|
||||
Layout& data = *m_layoutp;
|
||||
uint64_t tuples = std::any_of(cps, cps + dims,
|
||||
[](const VlCoverpoint* cpp) { return !cpp->normalBinCount(); })
|
||||
? 0
|
||||
: 1;
|
||||
for (uint32_t i = 0; i < dims; ++i) tuples *= cps[i]->normalBinCount();
|
||||
// Verilation bounds the product of the declared bins, which live bins cannot exceed.
|
||||
assert(tuples <= UINT32_MAX);
|
||||
data.m_tuples = static_cast<uint32_t>(tuples);
|
||||
data.m_words = VL_BITWORD_Q(static_cast<uint64_t>(data.m_tuples) + VL_QUADSIZE - 1);
|
||||
data.m_dimensions.resize(dims);
|
||||
data.m_counts.resize(data.m_tuples, 0);
|
||||
shape(dims, data.m_tuples);
|
||||
bindStorage(data.m_dimensions.data(), data.m_counts.data());
|
||||
VlCoverCross::init(hier, dims, cps, file, line, col);
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectAll() {
|
||||
Layout& data = *m_layoutp;
|
||||
data.m_stack.emplace_back(data.m_words, ~uint64_t{0});
|
||||
if (data.m_words) data.m_stack.back().back() &= VL_MASK_Q(data.m_tuples);
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated,
|
||||
bool intersect) {
|
||||
Layout& data = *m_layoutp;
|
||||
data.m_selectDimension = dim;
|
||||
data.m_selectFirst = first;
|
||||
data.m_selectEnd = end;
|
||||
data.m_negate = negated;
|
||||
data.m_allowed.assign(data.m_dimensions[dim].bins, false);
|
||||
if (!intersect) {
|
||||
for (uint32_t i = 0; i < data.m_allowed.size(); ++i) data.m_allowed[i] = data.named(i);
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectRange(QData lo, QData hi) {
|
||||
VlWide<VL_WQ_WORDS_E> low;
|
||||
VlWide<VL_WQ_WORDS_E> high;
|
||||
VL_SET_WQ(low, lo);
|
||||
VL_SET_WQ(high, hi);
|
||||
m_layoutp->range(low, high);
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectRangeW(WDataInP lop, WDataInP hip) { m_layoutp->range(lop, hip); }
|
||||
|
||||
void VlCoverCrossDyn::selectDimEnd() {
|
||||
Layout& data = *m_layoutp;
|
||||
data.m_stack.emplace_back(data.m_words, 0);
|
||||
Layout::Mask& mask = data.m_stack.back();
|
||||
const Dimension& dim = data.m_dimensions[data.m_selectDimension];
|
||||
// Each run of adjacent selected bins covers one contiguous tuple span per period.
|
||||
std::vector<std::pair<uint32_t, uint32_t>> runs; // [first, end) bin indices
|
||||
for (uint32_t i = 0; i < dim.bins;) {
|
||||
if (data.m_allowed[i] == data.m_negate) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const uint32_t begin = i++;
|
||||
while (i < dim.bins && data.m_allowed[i] != data.m_negate) ++i;
|
||||
runs.emplace_back(begin, i);
|
||||
}
|
||||
const uint64_t stride = dim.stride;
|
||||
const uint64_t period = stride * dim.bins;
|
||||
for (uint64_t base = 0; base < data.m_tuples; base += period) {
|
||||
for (const auto& run : runs) {
|
||||
Layout::setRange(mask, base + run.first * stride, base + run.second * stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectAnd() {
|
||||
Layout& data = *m_layoutp;
|
||||
Layout::Mask rhs = std::move(data.m_stack.back());
|
||||
data.m_stack.pop_back();
|
||||
for (uint32_t word = 0; word < data.m_words; ++word) data.m_stack.back()[word] &= rhs[word];
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectOr() {
|
||||
Layout& data = *m_layoutp;
|
||||
Layout::Mask rhs = std::move(data.m_stack.back());
|
||||
data.m_stack.pop_back();
|
||||
for (uint32_t word = 0; word < data.m_words; ++word) data.m_stack.back()[word] |= rhs[word];
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line,
|
||||
int col, uint32_t iffIndex) {
|
||||
Layout& data = *m_layoutp;
|
||||
if (VL_UNLIKELY(data.m_limit != Layout::Search::EMPTY)) {
|
||||
// Ignore a bin whose selection cannot be analyzed, rather than stop the simulation.
|
||||
VL_WARN_MT(
|
||||
filep, line, "",
|
||||
data.m_limit == Layout::Search::WORK_LIMIT
|
||||
? "Cross bin selection exceeds the decision-graph work limit; bin ignored"
|
||||
: "Cross bin selection exceeds the decision-graph depth limit; bin ignored");
|
||||
data.m_limit = Layout::Search::EMPTY;
|
||||
data.m_stack.pop_back();
|
||||
return;
|
||||
}
|
||||
Bin bin{};
|
||||
bin.kind = kind;
|
||||
bin.namep = namep;
|
||||
bin.filep = filep;
|
||||
bin.line = line;
|
||||
bin.col = col;
|
||||
bin.iffIndex = iffIndex;
|
||||
data.m_selected.push_back({bin, std::move(data.m_stack.back())});
|
||||
data.m_stack.pop_back();
|
||||
}
|
||||
|
||||
void VlCoverCrossDyn::finalizeBins() {
|
||||
Layout& data = *m_layoutp;
|
||||
Layout::Mask excluded(data.m_words, 0);
|
||||
for (const Layout::Selected& bin : data.m_selected) {
|
||||
if (bin.m_info.kind == VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (uint32_t word = 0; word < data.m_words; ++word) excluded[word] |= bin.m_mask[word];
|
||||
}
|
||||
for (Layout::Selected& bin : data.m_selected) {
|
||||
if (bin.m_info.kind != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (uint32_t word = 0; word < data.m_words; ++word) bin.m_mask[word] &= ~excluded[word];
|
||||
}
|
||||
data.m_selected.erase(std::remove_if(data.m_selected.begin(), data.m_selected.end(),
|
||||
[](const Layout::Selected& bin) {
|
||||
return std::all_of(
|
||||
bin.m_mask.begin(), bin.m_mask.end(),
|
||||
[](uint64_t word) { return !word; });
|
||||
}),
|
||||
data.m_selected.end());
|
||||
if (data.m_selected.empty()) return;
|
||||
Layout::Mask occupied(data.m_words, 0);
|
||||
uint64_t binWords = 0;
|
||||
for (const Layout::Selected& bin : data.m_selected) {
|
||||
for (uint32_t word = 0; word < data.m_words; ++word) {
|
||||
occupied[word] |= bin.m_mask[word];
|
||||
if (bin.m_mask[word]) ++binWords;
|
||||
}
|
||||
}
|
||||
// Selection masks have no bits past m_tuples, so this counts the automatic bins.
|
||||
uint32_t autoBins = data.m_tuples;
|
||||
for (const uint64_t word : occupied) autoBins -= VL_COUNTONES_Q(word);
|
||||
data.m_autoBins.resize(autoBins);
|
||||
data.m_bins.resize(data.m_selected.size());
|
||||
data.m_hitWords.resize(data.m_words);
|
||||
data.m_binWords.resize(binWords);
|
||||
data.m_selections.resize(data.m_selected.size() * data.m_words);
|
||||
data.m_explicitData = {{data.m_bins.data(), data.m_bins.size()},
|
||||
data.m_hitWords.data(),
|
||||
{data.m_autoBins.data(), data.m_autoBins.size()},
|
||||
{data.m_binWords.data(), data.m_binWords.size()},
|
||||
data.m_selections.data()};
|
||||
bindStorage(data.m_dimensions.data(), data.m_counts.data(), &data.m_explicitData);
|
||||
for (const Layout::Selected& bin : data.m_selected) {
|
||||
addBinImpl(bin.m_info.kind, bin.m_mask.data(), data.m_words, bin.m_info.namep,
|
||||
bin.m_info.filep, bin.m_info.line, bin.m_info.col, bin.m_info.iffIndex);
|
||||
}
|
||||
VlCoverCross::finalizeBins();
|
||||
data.m_selected.clear();
|
||||
data.m_stack.clear();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupInst
|
||||
|
||||
VlCoverCrossDyn* VlCovergroupInst::addCrossDyn() {
|
||||
VlCoverCrossDyn* const cxp = new VlCoverCrossDyn{};
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupType / VlCovRegistry
|
||||
|
||||
|
||||
@@ -99,6 +99,10 @@ public:
|
||||
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
|
||||
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
|
||||
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
|
||||
struct ValueData;
|
||||
std::unique_ptr<ValueData> m_valuesp; // Optional value metadata and exclusion state
|
||||
friend class VlCoverCrossDyn;
|
||||
|
||||
protected:
|
||||
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
|
||||
std::string m_hier; // "covergroup.coverpoint"
|
||||
@@ -120,10 +124,15 @@ private:
|
||||
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col);
|
||||
// Declared bin index of the i-th bin reported through VlCoverpointIf
|
||||
uint32_t reportedBin(uint32_t i) const;
|
||||
std::string declaredBinName(uint32_t bin) const; // Name of a declared bin index
|
||||
bool liveBin(uint32_t bin) const; // Normal bin keeps a value outside the exclusions
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverpoint() = default;
|
||||
VlCoverpoint();
|
||||
~VlCoverpoint() override;
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
@@ -137,6 +146,22 @@ public:
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
/// Configure construction-time value metadata for exclusions and cross selections.
|
||||
void valueType(uint32_t bits, bool isSigned);
|
||||
/// Describe bin values as {bin, low words, high words} entries, without enumerating them.
|
||||
void valueRanges(std::initializer_list<EData> entries);
|
||||
/// Describe wildcard patterns as {bin, value words, mask words, low words, high words}.
|
||||
void valuePatterns(std::initializer_list<EData> entries);
|
||||
/// State exclusions do not remove values from these transition bins.
|
||||
void valueTransitions(std::initializer_list<uint32_t> bins);
|
||||
/// Apply exclusions and freeze the live Normal-bin index space used by crosses.
|
||||
void valueFinalize();
|
||||
/// Drop the per-bin values once every cross has been built; sampling needs only exclusions.
|
||||
void valueRelease();
|
||||
/// Test state exclusions independently of sampling-time iff guards.
|
||||
bool valueExcluded(QData value) const;
|
||||
bool valueExcludedW(WDataInP valuep) const;
|
||||
|
||||
// ---- hot path (from generated sample()) ----
|
||||
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
|
||||
void clearHitList() { m_hitCount = 0; }
|
||||
@@ -152,19 +177,17 @@ public:
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
/// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported.
|
||||
uint32_t binCount() const override;
|
||||
std::string binName(uint32_t i) const override;
|
||||
// Deliberately not on VlCoverpointIf: only coverage-database registration needs it.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(i)).set(); }
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||
// path (incrementBin) stays a plain counter bump.
|
||||
uint32_t numCovered = 0;
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
for (const uint32_t bin : m_crossToBin) {
|
||||
if (m_counts[bin] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
covered = numCovered;
|
||||
total = m_normal;
|
||||
@@ -211,7 +234,7 @@ public:
|
||||
/// built on demand for automatic bins; explicit bins select sets of tuples
|
||||
/// and replace the corresponding automatic cross bins. Explicit selections
|
||||
/// are intersected with hit-tuple words once per sample.
|
||||
/// VlCoverCrossT owns the fixed arrays. This shared core does not allocate bin
|
||||
/// VlCoverCrossT and VlCoverCrossDyn own their storage. This shared core does not allocate bin
|
||||
/// storage, and its borrowed storage pointers remain valid for the instance.
|
||||
|
||||
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
|
||||
@@ -223,15 +246,16 @@ protected:
|
||||
uint32_t stride; // Flat-index stride
|
||||
};
|
||||
struct Bin final {
|
||||
const uint64_t* selectionp; // Slice of the fixed selection storage
|
||||
const uint64_t* selectionp; // Slice of the cross's selection storage
|
||||
const char* namep; // Explicit bin name
|
||||
const char* filep; // Bin declaration file
|
||||
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
|
||||
int line; // Bin declaration line
|
||||
int col; // Bin declaration column
|
||||
VlCovBinKind kind = VlCovBinKind::KIND_NORMAL; // Normal, ignore, or illegal bin
|
||||
uint32_t count = 0; // Samples matching the selection and guard
|
||||
uint32_t numWords = 0; // Number of nonzero selection-word indices
|
||||
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
|
||||
uint32_t iffIndex = 0; // Original guard index, including bins removed during finalization
|
||||
};
|
||||
struct Word final {
|
||||
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
|
||||
@@ -278,7 +302,7 @@ private:
|
||||
// storable anyway: m_flatCountsp alone would need 16GB.
|
||||
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
|
||||
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
|
||||
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by VlCoverCrossT
|
||||
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by the concrete cross runtime
|
||||
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
|
||||
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
|
||||
|
||||
@@ -326,19 +350,25 @@ protected:
|
||||
m_flatCountsp = countsp;
|
||||
m_explicitp = explicitp;
|
||||
}
|
||||
void shape(uint32_t dims, uint32_t tuples) {
|
||||
m_dims = dims;
|
||||
m_numAutoBins = tuples;
|
||||
}
|
||||
void addBinImpl(VlCovBinKind kind, const uint64_t* selectionp, uint32_t words,
|
||||
const char* namep, const char* filep, int line, int col, uint32_t iffIndex);
|
||||
|
||||
public:
|
||||
VL_UNCOPYABLE(VlCoverCross);
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor, after coverpoints init'd) ----
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
virtual void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples.
|
||||
void addBin(VlCovBinKind kind, std::initializer_list<uint64_t> selection, const char* namep,
|
||||
const char* filep, int line, int col);
|
||||
/// Retain only automatic cross bins not selected by any explicit bin.
|
||||
void finalizeBins();
|
||||
virtual void finalizeBins();
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
@@ -406,6 +436,36 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Construction-time cross layout over finalized coverpoints, sharing the sampling core.
|
||||
class VlCoverCrossDyn final : public VlCoverCross {
|
||||
class Layout;
|
||||
std::unique_ptr<Layout> m_layoutp; // Owned cross storage and construction-time selections
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCrossDyn();
|
||||
~VlCoverCrossDyn() override;
|
||||
|
||||
// METHODS
|
||||
/// Initialize after all feeding coverpoints have finalized their live bins.
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col) override;
|
||||
/// Build cross-bin selections in postfix order.
|
||||
void selectAll();
|
||||
/// Start a binsof term over the live bins declared in [first, end) of dimension 'dim'.
|
||||
void selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated, bool intersect);
|
||||
void selectRange(QData lo, QData hi);
|
||||
void selectRangeW(WDataInP lop, WDataInP hip);
|
||||
void selectDimEnd();
|
||||
void selectAnd();
|
||||
void selectOr();
|
||||
// Save a selection without renumbering guards when empty bins are removed.
|
||||
void selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line, int col,
|
||||
uint32_t iffIndex);
|
||||
/// Apply cross exclusions and bind finalized storage to the sampling core.
|
||||
void finalizeBins() override;
|
||||
};
|
||||
|
||||
class VlCovergroupType;
|
||||
|
||||
//=============================================================================
|
||||
@@ -456,6 +516,7 @@ public:
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp; // borrowed by the generated class
|
||||
}
|
||||
VlCoverCrossDyn* addCrossDyn();
|
||||
|
||||
// ---- attach counting (from VlCovInstHandle) ----
|
||||
void attachInc() { ++m_attachCount; }
|
||||
|
||||
@@ -874,6 +874,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_ADD_BIN, "addBin", false, "r+") \
|
||||
macro(COVERGROUP_ADD_COVERPOINT, "addCoverpoint", false, "") \
|
||||
macro(COVERGROUP_ADD_CROSS, "addCross", false, "") \
|
||||
macro(COVERGROUP_ADD_CROSS_DYN, "addCrossDyn", false, "") \
|
||||
macro(COVERGROUP_ADD_SINGLE_NAMER, "addSingleNamer", false, "r+") \
|
||||
macro(COVERGROUP_ATTACH, "attach", false, "r") \
|
||||
macro(COVERGROUP_CLEAR_HIT_LIST, "clearHitList", false, "") \
|
||||
@@ -886,6 +887,22 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
|
||||
macro(COVERGROUP_SAMPLE, "sample", false, "") \
|
||||
macro(COVERGROUP_SAMPLE_IFFS, "sample", false, "r") \
|
||||
macro(COVERGROUP_SELECT_ALL, "selectAll", false, "") \
|
||||
macro(COVERGROUP_SELECT_AND, "selectAnd", false, "") \
|
||||
macro(COVERGROUP_SELECT_BIN, "selectBin", false, "r+") \
|
||||
macro(COVERGROUP_SELECT_DIM, "selectDim", false, "r+") \
|
||||
macro(COVERGROUP_SELECT_DIM_END, "selectDimEnd", false, "") \
|
||||
macro(COVERGROUP_SELECT_OR, "selectOr", false, "") \
|
||||
macro(COVERGROUP_SELECT_RANGE, "selectRange", false, "rr") \
|
||||
macro(COVERGROUP_SELECT_RANGE_W, "selectRangeW", false, "rr") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED, "valueExcluded", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED_W, "valueExcludedW", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_FINALIZE, "valueFinalize", false, "") \
|
||||
macro(COVERGROUP_VALUE_PATTERNS, "valuePatterns", false, "r") \
|
||||
macro(COVERGROUP_VALUE_RANGES, "valueRanges", false, "r") \
|
||||
macro(COVERGROUP_VALUE_RELEASE, "valueRelease", false, "") \
|
||||
macro(COVERGROUP_VALUE_TRANSITIONS, "valueTransitions", false, "r") \
|
||||
macro(COVERGROUP_VALUE_TYPE, "valueType", false, "rr") \
|
||||
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
|
||||
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
|
||||
macro(DYN_CLEAR, "clear", false, "") \
|
||||
|
||||
+10
-6
@@ -691,22 +691,24 @@ public:
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstCoverCrossDType final : public AstNodeDType {
|
||||
// Borrowed pointer to VlCoverCrossT<dimensions, tuples, bins, autoBins, binWords>.
|
||||
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
|
||||
const uint32_t m_dimensions;
|
||||
const uint32_t m_tuples; // Fixed capacity number of cross tuples (VlCoverCrossT's Tuples)
|
||||
const uint32_t m_bins; // Fixed capacity number of explicit bins (VlCoverCrossT's Bins)
|
||||
const uint32_t m_autoBins; // Fixed capacity number of auto bins (VlCoverCrossT's AutoBins)
|
||||
const uint64_t m_binWords; // Fixed capacity selection words (VlCoverCrossT's BinWords)
|
||||
const bool m_dynamic; // Bin layout is determined at covergroup construction
|
||||
|
||||
public:
|
||||
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
|
||||
uint32_t autoBins, uint64_t binWords)
|
||||
uint32_t autoBins, uint64_t binWords, bool dynamic = false)
|
||||
: ASTGEN_SUPER_CoverCrossDType(fl)
|
||||
, m_dimensions{dimensions}
|
||||
, m_tuples{tuples}
|
||||
, m_bins{bins}
|
||||
, m_autoBins{autoBins}
|
||||
, m_binWords{binWords} {
|
||||
, m_binWords{binWords}
|
||||
, m_dynamic{dynamic} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCrossDType;
|
||||
@@ -714,11 +716,12 @@ public:
|
||||
BROKEN_RTN(m_dimensions == 0);
|
||||
return nullptr;
|
||||
}
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverCrossDType* const sp = VN_DBG_AS(samep, CoverCrossDType);
|
||||
return dimensions() == sp->dimensions() && tuples() == sp->tuples() && bins() == sp->bins()
|
||||
&& autoBins() == sp->autoBins() && binWords() == sp->binWords();
|
||||
}
|
||||
&& autoBins() == sp->autoBins() && binWords() == sp->binWords()
|
||||
&& isDynamic() == sp->isDynamic();
|
||||
} // LCOV_EXCL_STOP
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
@@ -728,6 +731,7 @@ public:
|
||||
uint32_t bins() const { return m_bins; }
|
||||
uint32_t autoBins() const { return m_autoBins; }
|
||||
uint64_t binWords() const { return m_binWords; }
|
||||
bool isDynamic() const { return m_dynamic; }
|
||||
string cppTemplateArgs() const;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return sizeof(void*); }
|
||||
|
||||
+4
-1
@@ -1224,6 +1224,7 @@ string AstCoverCrossDType::cppTemplateArgs() const {
|
||||
void AstCoverCrossDType::dump(std::ostream& str) const {
|
||||
Super::dump(str);
|
||||
str << " [" << cppTemplateArgs() << "]";
|
||||
if (isDynamic()) str << " [DYNAMIC]";
|
||||
}
|
||||
void AstCoverCrossDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonNumFunc(str, dimensions);
|
||||
@@ -1231,6 +1232,7 @@ void AstCoverCrossDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonNumFunc(str, bins);
|
||||
dumpJsonNumFunc(str, autoBins);
|
||||
dumpJsonNumFunc(str, binWords);
|
||||
dumpJsonBoolFuncIf(str, isDynamic);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCoverCrossDType::dumpSmall(std::ostream& str) const {
|
||||
@@ -2182,7 +2184,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CoverCrossDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlCoverCrossT<" + adtypep->cppTemplateArgs() + ">*";
|
||||
info.m_type = adtypep->isDynamic() ? "VlCoverCrossDyn*"
|
||||
: "VlCoverCrossT<" + adtypep->cppTemplateArgs() + ">*";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CoverpointDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlCoverpointT<" + cvtToStr(adtypep->hitBound()) + ">*";
|
||||
|
||||
+681
-468
File diff suppressed because it is too large
Load Diff
@@ -12,16 +12,18 @@ cg2.cp_data3.auto_2: 1
|
||||
cg2.cp_data3.auto_3: 0
|
||||
cg3.cp_data3.auto_0: 1
|
||||
cg3.cp_data3.auto_1: 1
|
||||
cg4.cp.auto_0: 0
|
||||
cg4.cp.auto_1: 1
|
||||
cg4.cp.auto_2: 1
|
||||
cg4.cp.auto_3: 1
|
||||
cg4.cp.ign [ignore]: 0
|
||||
cg5.cp_data64.auto_0: 1
|
||||
cg5.cp_data64.auto_1: 1
|
||||
cg5.cp_data64.auto_2: 1
|
||||
cg5.cp_data64.auto_0: 2
|
||||
cg5.cp_data64.auto_1: 0
|
||||
cg5.cp_data64.auto_2: 0
|
||||
cg5.cp_data64.auto_3: 1
|
||||
cg6.cp_data3.auto_0: 1
|
||||
cg6.cp_data3.auto_1: 0
|
||||
cg6.cp_data3.auto_2: 0
|
||||
cg6.cp_data3.auto_3: 1
|
||||
cg7.cp_sdata3.auto_0: 1
|
||||
cg7.cp_sdata3.auto_1: 1
|
||||
cg7.cp_sdata3.auto_2: 2
|
||||
|
||||
@@ -16,6 +16,7 @@ module t;
|
||||
logic [2:0] data3;
|
||||
logic [3:0] data4;
|
||||
logic [63:0] data64; // 64-bit signal
|
||||
logic signed [2:0] sdata3;
|
||||
|
||||
// Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal
|
||||
covergroup cg1;
|
||||
@@ -58,6 +59,13 @@ module t;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
// Test 7: signed values are partitioned in value order, the last bin taking the remainder:
|
||||
// [-4:-3],[-2:-1],[0:3]
|
||||
covergroup cg7;
|
||||
option.auto_bin_max = 3;
|
||||
cp_sdata3: coverpoint sdata3;
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg1 cg1_inst;
|
||||
cg2 cg2_inst;
|
||||
@@ -65,6 +73,7 @@ module t;
|
||||
cg4 cg4_inst;
|
||||
cg5 cg5_inst;
|
||||
cg6 cg6_inst;
|
||||
cg7 cg7_inst;
|
||||
|
||||
cg1_inst = new;
|
||||
cg2_inst = new;
|
||||
@@ -72,6 +81,7 @@ module t;
|
||||
cg4_inst = new;
|
||||
cg5_inst = new;
|
||||
cg6_inst = new;
|
||||
cg7_inst = new;
|
||||
|
||||
data3 = 0;
|
||||
cg1_inst.sample();
|
||||
@@ -97,23 +107,25 @@ module t;
|
||||
|
||||
// Sample valid (non-ignored) values for cg4
|
||||
// cg4: auto_bin_max=4 creates 4 bins [0:3],[4:7],[8:11],[12:15].
|
||||
// ignore_bins ign={[0:3]} excludes [0:3] values; Verilator keeps all 4 bins in denominator.
|
||||
// 3 of 4 bins hit -> 75% (the [0:3] bin is included in denominator but can never be hit)
|
||||
// The empty [0:3] bin is excluded from the denominator (IEEE 1800-2023 19.11.1).
|
||||
data4 = 4;
|
||||
cg4_inst.sample(); // [4:7] bin
|
||||
data4 = 8;
|
||||
cg4_inst.sample(); // [8:11] bin
|
||||
data4 = 12;
|
||||
cg4_inst.sample(); // [12:15] bin
|
||||
`checkr(cg4_inst.get_inst_coverage(), 75.0);
|
||||
`checkr(cg4_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg5: 64-bit coverpoint - SKIP: Verilator 64-bit bin boundary bug causes 100% at first sample
|
||||
// Sample cg5: the full 64-bit domain is partitioned into four bins.
|
||||
data64 = 64'h0;
|
||||
cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 25.0);
|
||||
data64 = 64'h1111111111111111;
|
||||
cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 25.0);
|
||||
data64 = 64'hffffffffffffffff;
|
||||
cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 50.0);
|
||||
|
||||
data3 = 0;
|
||||
cg6_inst.sample();
|
||||
@@ -122,6 +134,17 @@ module t;
|
||||
cg6_inst.sample();
|
||||
`checkr(cg6_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[6:7]
|
||||
|
||||
sdata3 = -4;
|
||||
cg7_inst.sample(); // [-4:-3]
|
||||
sdata3 = 3;
|
||||
cg7_inst.sample(); // [0:3]
|
||||
`checkr(cg7_inst.get_inst_coverage(), 100.0 * 2.0 / 3.0);
|
||||
sdata3 = 0;
|
||||
cg7_inst.sample(); // [0:3]
|
||||
sdata3 = -1;
|
||||
cg7_inst.sample(); // [-2:-1]
|
||||
`checkr(cg7_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
cg_cross.aa.auto_1: 4
|
||||
cg_cross.aa.auto_2: 5
|
||||
cg_cross.aa.high [illegal]: 0
|
||||
cg_cross.aa.low [ignore]: 0
|
||||
cg_cross.automatic_cross.auto_1_x_values[0] [cross]: 1
|
||||
cg_cross.automatic_cross.auto_1_x_values[2] [cross]: 1
|
||||
cg_cross.automatic_cross.auto_1_x_values[3] [cross]: 1
|
||||
cg_cross.automatic_cross.auto_2_x_values[0] [cross]: 1
|
||||
cg_cross.automatic_cross.auto_2_x_values[2] [cross]: 2
|
||||
cg_cross.automatic_cross.auto_2_x_values[3] [cross]: 1
|
||||
cg_cross.bb.ignored [ignore]: 2
|
||||
cg_cross.bb.values[0]: 2
|
||||
cg_cross.bb.values[2]: 3
|
||||
cg_cross.bb.values[3]: 2
|
||||
cg_cross.xx.all [cross]: 2
|
||||
cg_cross.xx.column [cross]: 1
|
||||
cg_cross.xx.forbidden [illegal,cross]: 0
|
||||
cg_cross.xx.grouped [cross]: 3
|
||||
cg_cross.xx.ignored [ignore,cross]: 1
|
||||
cg_cross.xx.row [cross]: 1
|
||||
cg_empty.aa.ignored [ignore]: 0
|
||||
cg_empty.bb.auto_0: 0
|
||||
cg_empty.bb.auto_1: 1
|
||||
cg_empty.bb.auto_2: 1
|
||||
cg_empty.bb.auto_3: 0
|
||||
cg_explicit.cp.all: 5
|
||||
cg_explicit.cp.forbidden [illegal]: 0
|
||||
cg_explicit.cp.ignored[0] [ignore]: 1
|
||||
cg_explicit.cp.ignored[1] [ignore]: 1
|
||||
cg_explicit.cp.mixed: 1
|
||||
cg_explicit.cp.values[1]: 1
|
||||
cg_explicit.cp.values[2]: 1
|
||||
cg_many_values.cp.ignored [ignore]: 0
|
||||
cg_many_values.cp.odd: 2
|
||||
cg_partition.cp.auto_1: 2
|
||||
cg_partition.cp.auto_2: 1
|
||||
cg_partition.cp.auto_3: 1
|
||||
cg_partition.cp.ignored [ignore]: 4
|
||||
cg_pattern_carry.cp_signed.ignored [ignore]: 1
|
||||
cg_pattern_carry.cp_signed.patterned: 4
|
||||
cg_pattern_carry.cp_unsigned.ignored [ignore]: 1
|
||||
cg_pattern_carry.cp_unsigned.patterned: 4
|
||||
cg_pattern_carry.other.auto_0: 8
|
||||
cg_pattern_carry.other.auto_1: 8
|
||||
cg_pattern_carry.signed_cross.carry [cross]: 4
|
||||
cg_pattern_carry.unsigned_cross.carry [cross]: 4
|
||||
cg_projection.negative.keep: 1
|
||||
cg_projection.negative.positive [ignore]: 1
|
||||
cg_projection.positive.keep: 1
|
||||
cg_projection.positive.negative [ignore]: 1
|
||||
cg_real_point.cp.low: 1
|
||||
cg_real_point.cp.middle: 1
|
||||
cg_real_point.cp.three: 1
|
||||
cg_real_values.cp.high: 1
|
||||
cg_real_values.cp.ignored [ignore]: 1
|
||||
cg_real_values.cp.low: 1
|
||||
cg_real_values.cp.middle: 2
|
||||
cg_real_values.cp.two: 1
|
||||
cg_real_values.plain.middle: 2
|
||||
cg_real_values.plain.two: 1
|
||||
cg_resolution.cp.clipped: 4
|
||||
cg_resolution.cp.kept: 4
|
||||
cg_resolution.cp.rest [default]: 4
|
||||
cg_resolution.cp.values[0]: 2
|
||||
cg_resolution.cx.clipped_x_auto_0 [cross]: 2
|
||||
cg_resolution.cx.clipped_x_auto_1 [cross]: 2
|
||||
cg_resolution.cx.kept_values [cross]: 4
|
||||
cg_resolution.cx.values[0]_x_auto_0 [cross]: 1
|
||||
cg_resolution.cx.values[0]_x_auto_1 [cross]: 1
|
||||
cg_resolution.other.auto_0: 7
|
||||
cg_resolution.other.auto_1: 7
|
||||
cg_resolution_excl.cp.clipped: 4
|
||||
cg_resolution_excl.cp.kept: 4
|
||||
cg_resolution_excl.cp.rest [default]: 4
|
||||
cg_resolution_excl.cp.unused [ignore]: 0
|
||||
cg_resolution_excl.cp.values[0]: 2
|
||||
cg_resolution_excl.cx.clipped_x_auto_0 [cross]: 2
|
||||
cg_resolution_excl.cx.clipped_x_auto_1 [cross]: 2
|
||||
cg_resolution_excl.cx.kept_values [cross]: 4
|
||||
cg_resolution_excl.cx.values[0]_x_auto_0 [cross]: 1
|
||||
cg_resolution_excl.cx.values[0]_x_auto_1 [cross]: 1
|
||||
cg_resolution_excl.other.auto_0: 7
|
||||
cg_resolution_excl.other.auto_1: 7
|
||||
cg_signed.cp.auto_1: 1
|
||||
cg_signed.cp.auto_2: 1
|
||||
cg_signed.cp.ignored [ignore]: 1
|
||||
cg_signed_wide.cp.auto_1: 2
|
||||
cg_signed_wide.cp.auto_2: 1
|
||||
cg_signed_wide.cp.auto_3: 1
|
||||
cg_signed_wide.cp.ignored [ignore]: 1
|
||||
cg_signed_wide.cx.auto_2_x_auto_0 [cross]: 1
|
||||
cg_signed_wide.cx.auto_2_x_auto_1 [cross]: 0
|
||||
cg_signed_wide.cx.auto_3_x_auto_0 [cross]: 0
|
||||
cg_signed_wide.cx.negative [cross]: 2
|
||||
cg_signed_wide.cx.positive [cross]: 1
|
||||
cg_signed_wide.other.auto_0: 3
|
||||
cg_signed_wide.other.auto_1: 2
|
||||
cg_source_width.cp.ignored [ignore]: 0
|
||||
cg_source_width.cp.keep: 2
|
||||
cg_unsigned_pattern.cp.all_values: 1
|
||||
cg_unsigned_pattern.cp.ignored [ignore]: 0
|
||||
cg_unsigned_pattern.cp.negative: 1
|
||||
cg_unsigned_pattern.cp.negative_pair: 1
|
||||
cg_wide.cp.auto_1: 1
|
||||
cg_wide.cp.auto_2: 1
|
||||
cg_wide.cp.auto_3: 1
|
||||
cg_wide.cp.ignored [ignore]: 1
|
||||
cg_wide_cross.aa.all: 129
|
||||
cg_wide_cross.aa.high[0]: 2
|
||||
cg_wide_cross.aa.high[10]: 2
|
||||
cg_wide_cross.aa.high[11]: 2
|
||||
cg_wide_cross.aa.high[12]: 2
|
||||
cg_wide_cross.aa.high[13]: 2
|
||||
cg_wide_cross.aa.high[14]: 2
|
||||
cg_wide_cross.aa.high[15]: 2
|
||||
cg_wide_cross.aa.high[16]: 2
|
||||
cg_wide_cross.aa.high[17]: 2
|
||||
cg_wide_cross.aa.high[18]: 2
|
||||
cg_wide_cross.aa.high[19]: 2
|
||||
cg_wide_cross.aa.high[1]: 2
|
||||
cg_wide_cross.aa.high[20]: 2
|
||||
cg_wide_cross.aa.high[21]: 2
|
||||
cg_wide_cross.aa.high[22]: 2
|
||||
cg_wide_cross.aa.high[23]: 2
|
||||
cg_wide_cross.aa.high[24]: 2
|
||||
cg_wide_cross.aa.high[25]: 2
|
||||
cg_wide_cross.aa.high[26]: 2
|
||||
cg_wide_cross.aa.high[27]: 2
|
||||
cg_wide_cross.aa.high[28]: 2
|
||||
cg_wide_cross.aa.high[29]: 2
|
||||
cg_wide_cross.aa.high[2]: 2
|
||||
cg_wide_cross.aa.high[30]: 2
|
||||
cg_wide_cross.aa.high[31]: 2
|
||||
cg_wide_cross.aa.high[3]: 2
|
||||
cg_wide_cross.aa.high[4]: 2
|
||||
cg_wide_cross.aa.high[5]: 2
|
||||
cg_wide_cross.aa.high[6]: 2
|
||||
cg_wide_cross.aa.high[7]: 2
|
||||
cg_wide_cross.aa.high[8]: 2
|
||||
cg_wide_cross.aa.high[9]: 2
|
||||
cg_wide_cross.aa.ignored [ignore]: 2
|
||||
cg_wide_cross.aa.low[10]: 2
|
||||
cg_wide_cross.aa.low[11]: 2
|
||||
cg_wide_cross.aa.low[12]: 2
|
||||
cg_wide_cross.aa.low[13]: 2
|
||||
cg_wide_cross.aa.low[14]: 2
|
||||
cg_wide_cross.aa.low[15]: 2
|
||||
cg_wide_cross.aa.low[16]: 2
|
||||
cg_wide_cross.aa.low[17]: 2
|
||||
cg_wide_cross.aa.low[18]: 2
|
||||
cg_wide_cross.aa.low[19]: 2
|
||||
cg_wide_cross.aa.low[1]: 3
|
||||
cg_wide_cross.aa.low[20]: 2
|
||||
cg_wide_cross.aa.low[21]: 2
|
||||
cg_wide_cross.aa.low[22]: 2
|
||||
cg_wide_cross.aa.low[23]: 2
|
||||
cg_wide_cross.aa.low[24]: 2
|
||||
cg_wide_cross.aa.low[25]: 2
|
||||
cg_wide_cross.aa.low[26]: 2
|
||||
cg_wide_cross.aa.low[27]: 2
|
||||
cg_wide_cross.aa.low[28]: 2
|
||||
cg_wide_cross.aa.low[29]: 2
|
||||
cg_wide_cross.aa.low[2]: 2
|
||||
cg_wide_cross.aa.low[30]: 2
|
||||
cg_wide_cross.aa.low[31]: 2
|
||||
cg_wide_cross.aa.low[32]: 2
|
||||
cg_wide_cross.aa.low[3]: 2
|
||||
cg_wide_cross.aa.low[4]: 2
|
||||
cg_wide_cross.aa.low[5]: 2
|
||||
cg_wide_cross.aa.low[6]: 2
|
||||
cg_wide_cross.aa.low[7]: 2
|
||||
cg_wide_cross.aa.low[8]: 2
|
||||
cg_wide_cross.aa.low[9]: 2
|
||||
cg_wide_cross.aa.middle: 129
|
||||
cg_wide_cross.bb.auto_0: 66
|
||||
cg_wide_cross.bb.auto_1: 65
|
||||
cg_wide_cross.xx.disabled [cross]: 0
|
||||
cg_wide_cross.xx.enabled_bin [cross]: 128
|
||||
cg_wide_cross.xx.tail [cross]: 128
|
||||
cg_wild.cp.ignored [ignore]: 1
|
||||
cg_wild.cp.odd_values: 2
|
||||
cg_wild_empty.cp.even_values [ignore]: 2
|
||||
cg_wild_empty.cp.odd_values [ignore]: 2
|
||||
+19
@@ -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
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test,
|
||||
verilator_flags2=['--timing', '--dump-tree', '--dump-tree-json'],
|
||||
threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,384 @@
|
||||
// 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
|
||||
|
||||
// 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 (
|
||||
input clk
|
||||
);
|
||||
int cyc = 0;
|
||||
|
||||
// IEEE 1800-2023 19.11.1: retain auto_1={2,3}, auto_2={4}, auto_3={7}.
|
||||
covergroup cg_partition with function sample (bit [2:0] value);
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint value {
|
||||
ignore_bins ignored = {0, 1, 5, 6};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_explicit with function sample (bit [2:0] value);
|
||||
cp: coverpoint value {
|
||||
bins all = {[0 : 7]};
|
||||
bins empty = {0, 1};
|
||||
bins mixed = {1, 3, 5};
|
||||
bins values[] = {0, 2, 4};
|
||||
wildcard bins outside_domain = {4'b1???};
|
||||
ignore_bins ignored[] = {0, 1};
|
||||
illegal_bins forbidden = {5} iff (0);
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_cross with function sample (bit [3:0] a, bit [1:0] b, bit enabled);
|
||||
option.auto_bin_max = 4;
|
||||
aa: coverpoint a {
|
||||
ignore_bins low = {[0 : 3]};
|
||||
illegal_bins high = {[12 : 15]} iff (0);
|
||||
}
|
||||
bb: coverpoint b {
|
||||
bins values[] = {[0 : 3]};
|
||||
ignore_bins ignored = {1};
|
||||
}
|
||||
automatic_cross: cross aa, bb;
|
||||
xx: cross aa, bb{
|
||||
bins empty = binsof (aa) intersect {0} iff (0);
|
||||
bins removed = binsof (aa) intersect {8} && binsof (bb) intersect {3} iff (0);
|
||||
bins all = xx iff (enabled);
|
||||
bins row = binsof (aa) intersect {4} iff (!enabled);
|
||||
bins column = binsof (bb.values) intersect {2} iff (enabled);
|
||||
bins grouped = (xx && binsof (aa) intersect {8}) || binsof (bb) intersect {2};
|
||||
ignore_bins ignored = binsof (aa) intersect {8} && binsof (bb) intersect {3} iff (enabled);
|
||||
illegal_bins forbidden = binsof (aa) intersect {4} && binsof (bb) intersect {2} iff (0);
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_wide_cross with function sample (bit [6:0] a, bit b, bit enabled);
|
||||
aa: coverpoint a {
|
||||
bins low[] = {[0 : 32]};
|
||||
bins middle = {[0 : 64]};
|
||||
bins high[] = {[33 : 64]};
|
||||
bins all = {[0 : 64]};
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
bb: coverpoint b;
|
||||
xx: cross aa, bb{
|
||||
bins empty = binsof (aa.low) intersect {0} iff (0);
|
||||
bins disabled = xx iff (0);
|
||||
bins enabled_bin = xx iff (enabled);
|
||||
bins tail = binsof (aa.all) iff (enabled);
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_empty with function sample (bit [1:0] a, bit [1:0] b);
|
||||
aa: coverpoint a {
|
||||
ignore_bins ignored = {[0 : 3]} iff (0);
|
||||
}
|
||||
bb: coverpoint b;
|
||||
xx: cross aa, bb{bins empty = xx iff (0);}
|
||||
endgroup
|
||||
|
||||
covergroup cg_signed with function sample (bit signed [6:0] value);
|
||||
option.auto_bin_max = 3;
|
||||
cp: coverpoint value {
|
||||
ignore_bins ignored = {[-64 : -23]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_wide with function sample (bit [64:0] value);
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint value {
|
||||
ignore_bins ignored = {[65'h0 : 65'h0_7fffffffffffffff]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_wild with function sample (bit [64:0] value);
|
||||
cp: coverpoint value {
|
||||
wildcard bins even_values = {65'bx0};
|
||||
wildcard bins odd_values = {65'bx1};
|
||||
wildcard ignore_bins ignored = {65'bx0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_wild_empty with function sample (bit [64:0] value);
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint value {
|
||||
wildcard ignore_bins even_values = {65'bx0};
|
||||
wildcard ignore_bins odd_values = {65'bx1};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_projection with function sample (bit signed [2:0] value);
|
||||
negative: coverpoint value {
|
||||
bins keep = {3'sb111};
|
||||
wildcard bins empty = {5'b01???};
|
||||
wildcard bins signed_empty = {5'sb01???};
|
||||
wildcard ignore_bins positive = {4'sb0???};
|
||||
}
|
||||
positive: coverpoint value {
|
||||
bins keep = {3'sb001};
|
||||
wildcard ignore_bins negative = {4'sb1???};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_source_width with function sample (bit signed [6:0] value);
|
||||
cp: coverpoint value {
|
||||
wildcard bins keep = {2'sbx0};
|
||||
ignore_bins ignored = {1};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_unsigned_pattern with function sample (bit signed [2:0] value);
|
||||
cp: coverpoint value {
|
||||
wildcard bins all_values = {4'b0???};
|
||||
wildcard bins negative_pair = {4'b011?};
|
||||
bins negative = {4'b0111};
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_zero_limit with function sample (bit value);
|
||||
option.auto_bin_max = 0;
|
||||
cp: coverpoint value;
|
||||
endgroup
|
||||
|
||||
covergroup cg_signed_wide with function sample (bit signed [64:0] value, bit side);
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint value {
|
||||
ignore_bins ignored = {[$ : 65'sh1_7fffffffffffffff]};
|
||||
}
|
||||
other: coverpoint side;
|
||||
cx: cross cp, other{
|
||||
bins negative = binsof (cp) intersect {65'sh1_ffffffffffffffff};
|
||||
bins positive = binsof (cp) intersect {65'sh0_8000000000000000} && binsof (other) intersect {
|
||||
1
|
||||
};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_pattern_carry with function sample (
|
||||
bit [3:0] unsigned_value, bit signed [3:0] signed_value, bit side
|
||||
);
|
||||
cp_unsigned: coverpoint unsigned_value {
|
||||
wildcard bins patterned = {4'b?0?0};
|
||||
ignore_bins ignored = {15};
|
||||
}
|
||||
cp_signed: coverpoint signed_value {
|
||||
wildcard bins patterned = {4'sb?0?0};
|
||||
ignore_bins ignored = {4'shf};
|
||||
}
|
||||
other: coverpoint side;
|
||||
unsigned_cross: cross cp_unsigned, other{
|
||||
bins carry = binsof (cp_unsigned.patterned) intersect {[3 : 9]};
|
||||
bins empty = binsof (cp_unsigned.patterned) intersect {[11 : 15]};
|
||||
}
|
||||
signed_cross: cross cp_signed, other{
|
||||
bins carry = binsof (cp_signed.patterned) intersect {[-5 : 1]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Bin values resolve to the coverpoint's type (IEEE 1800-2023 19.5.7) and bins without
|
||||
// values do not count (19.11.1), whether or not the coverpoint has exclusions. The two
|
||||
// covergroups differ only by an ignore_bins that is never hit.
|
||||
// verilog_format: off
|
||||
`define RESOLUTION_BINS \
|
||||
bins outside = {[8 : 9]}; \
|
||||
bins unknown = {3'bx01}; \
|
||||
bins wide = {15}; \
|
||||
bins reversed = {[3 : 1]}; \
|
||||
bins kept = {[2 : 3]}; \
|
||||
bins clipped = {[6 : 10]}; \
|
||||
bins values[] = {4, 3'bx11}; \
|
||||
bins rest = default;
|
||||
`define RESOLUTION_CROSS \
|
||||
other: coverpoint side; \
|
||||
cx: cross cp, other { \
|
||||
bins unknown_values = binsof (cp) intersect {3'bx01}; \
|
||||
bins kept_values = binsof (cp.kept); \
|
||||
}
|
||||
// verilog_format: on
|
||||
covergroup cg_resolution with function sample (logic [2:0] value, bit side);
|
||||
cp: coverpoint value {`RESOLUTION_BINS}
|
||||
`RESOLUTION_CROSS
|
||||
endgroup
|
||||
|
||||
covergroup cg_resolution_excl with function sample (logic [2:0] value, bit side);
|
||||
cp: coverpoint value {
|
||||
`RESOLUTION_BINS
|
||||
ignore_bins unused = {5};
|
||||
}
|
||||
`RESOLUTION_CROSS
|
||||
endgroup
|
||||
|
||||
// Real bin values keep the integral values they contain (IEEE 1800-2023 19.5.7).
|
||||
covergroup cg_real_values with function sample (bit [2:0] value);
|
||||
cp: coverpoint value {
|
||||
bins two = {2.0};
|
||||
bins fraction = {2.5};
|
||||
bins far = {100.0};
|
||||
bins middle = {[3.5 : 5.5]};
|
||||
bins high = {[6.5 : 100.0]};
|
||||
bins low = {[-5.0 : 0.5]};
|
||||
ignore_bins ignored = {1.0};
|
||||
}
|
||||
plain: coverpoint value {
|
||||
bins two = {2.0};
|
||||
bins middle = {[3.5 : 5.5]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_real_point with function sample (real value);
|
||||
cp: coverpoint value {
|
||||
bins low = {[0 : 1]};
|
||||
bins middle = {[1.5 : 2.5]};
|
||||
bins three = {3.0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// More values than one constructor call describes
|
||||
covergroup cg_many_values with function sample (bit [9:0] value);
|
||||
cp: coverpoint value {
|
||||
bins odd = {
|
||||
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29,
|
||||
31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59,
|
||||
61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89,
|
||||
91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119,
|
||||
121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149,
|
||||
151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179,
|
||||
181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209,
|
||||
211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239,
|
||||
241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269,
|
||||
271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299,
|
||||
301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329,
|
||||
331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359,
|
||||
361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383, 385, 387, 389,
|
||||
391, 393, 395, 397, 399, 401, 403, 405, 407, 409, 411, 413, 415, 417, 419,
|
||||
421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449,
|
||||
451, 453, 455, 457, 459, 461, 463, 465, 467, 469, 471, 473, 475, 477, 479,
|
||||
481, 483, 485, 487, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507, 509,
|
||||
511, 513, 515, 517, 519, 521, 523, 525, 527, 529, 531, 533, 535, 537, 539,
|
||||
541, 543, 545, 547, 549, 551, 553, 555, 557, 559, 561, 563, 565, 567, 569,
|
||||
571, 573, 575, 577, 579, 581, 583, 585, 587, 589, 591, 593, 595, 597, 599
|
||||
};
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg_partition partition_cov = new;
|
||||
cg_explicit explicit_cov = new;
|
||||
cg_cross cross_cov = new;
|
||||
cg_wide_cross wide_cross_cov = new;
|
||||
cg_empty empty_cov = new;
|
||||
cg_signed signed_cov = new;
|
||||
cg_wide wide_cov = new;
|
||||
cg_wild wild_cov = new;
|
||||
cg_wild_empty wild_empty_cov = new;
|
||||
cg_projection projection_cov = new;
|
||||
cg_source_width source_width_cov = new;
|
||||
cg_unsigned_pattern unsigned_pattern_cov = new;
|
||||
cg_zero_limit zero_limit_cov = new;
|
||||
cg_signed_wide signed_wide_cov = new;
|
||||
cg_pattern_carry pattern_carry_cov = new;
|
||||
cg_resolution resolution_cov = new;
|
||||
cg_resolution_excl resolution_excl_cov = new;
|
||||
cg_real_values real_values_cov = new;
|
||||
cg_real_point real_point_cov = new;
|
||||
cg_many_values many_values_cov = new;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (cyc == 0) begin
|
||||
partition_cov.sample(2);
|
||||
partition_cov.sample(4);
|
||||
partition_cov.sample(7);
|
||||
`checkr(partition_cov.get_inst_coverage(), 100.0);
|
||||
partition_cov.sample(3);
|
||||
explicit_cov.sample(2);
|
||||
explicit_cov.sample(3);
|
||||
explicit_cov.sample(4);
|
||||
`checkr(explicit_cov.get_inst_coverage(), 100.0);
|
||||
empty_cov.sample(0, 1);
|
||||
empty_cov.sample(3, 2);
|
||||
`checkr(empty_cov.get_inst_coverage(), 50.0);
|
||||
signed_cov.sample(-22);
|
||||
signed_cov.sample(63);
|
||||
`checkr(signed_cov.get_inst_coverage(), 100.0);
|
||||
signed_cov.sample(-64);
|
||||
wide_cov.sample(65'h0_8000000000000000);
|
||||
wide_cov.sample(65'h1_0000000000000000);
|
||||
wide_cov.sample(65'h1_ffffffffffffffff);
|
||||
`checkr(wide_cov.get_inst_coverage(), 100.0);
|
||||
wide_cov.sample(0);
|
||||
wild_cov.sample(1);
|
||||
wild_cov.sample(65'h1_ffffffffffffffff);
|
||||
wild_cov.sample(0);
|
||||
`checkr(wild_cov.get_inst_coverage(), 100.0);
|
||||
wild_empty_cov.sample(0);
|
||||
wild_empty_cov.sample(1);
|
||||
wild_empty_cov.sample(65'h1_fffffffffffffffe);
|
||||
wild_empty_cov.sample(65'h1_ffffffffffffffff);
|
||||
projection_cov.sample(-1);
|
||||
`checkr(projection_cov.get_inst_coverage(), 50.0);
|
||||
projection_cov.sample(1);
|
||||
`checkr(projection_cov.get_inst_coverage(), 100.0);
|
||||
source_width_cov.sample(-4);
|
||||
source_width_cov.sample(2);
|
||||
`checkr(source_width_cov.get_inst_coverage(), 0.0);
|
||||
source_width_cov.sample(-2);
|
||||
source_width_cov.sample(0);
|
||||
`checkr(source_width_cov.get_inst_coverage(), 100.0);
|
||||
unsigned_pattern_cov.sample(-1);
|
||||
`checkr(unsigned_pattern_cov.get_inst_coverage(), 100.0);
|
||||
zero_limit_cov.sample(0);
|
||||
zero_limit_cov.sample(1);
|
||||
signed_wide_cov.sample(-1, 0);
|
||||
signed_wide_cov.sample(-1, 1);
|
||||
signed_wide_cov.sample(0, 0);
|
||||
signed_wide_cov.sample(65'sh0_ffffffffffffffff, 1);
|
||||
signed_wide_cov.sample(65'sh1_0000000000000000, 0);
|
||||
for (int value = 0; value < 8; ++value) begin
|
||||
if (value != 5) begin
|
||||
resolution_cov.sample(3'(value), 0);
|
||||
resolution_cov.sample(3'(value), 1);
|
||||
resolution_excl_cov.sample(3'(value), 0);
|
||||
resolution_excl_cov.sample(3'(value), 1);
|
||||
end
|
||||
real_values_cov.sample(3'(value));
|
||||
end
|
||||
`checkr(resolution_cov.get_inst_coverage(), 100.0);
|
||||
`checkr(resolution_excl_cov.get_inst_coverage(), 100.0);
|
||||
`checkr(real_values_cov.get_inst_coverage(), 100.0);
|
||||
real_point_cov.sample(1.25);
|
||||
`checkr(real_point_cov.get_inst_coverage(), 0.0);
|
||||
real_point_cov.sample(0.5);
|
||||
real_point_cov.sample(2.0);
|
||||
real_point_cov.sample(3.0);
|
||||
`checkr(real_point_cov.get_inst_coverage(), 100.0);
|
||||
many_values_cov.sample(2);
|
||||
`checkr(many_values_cov.get_inst_coverage(), 0.0);
|
||||
many_values_cov.sample(599);
|
||||
many_values_cov.sample(1);
|
||||
`checkr(many_values_cov.get_inst_coverage(), 100.0);
|
||||
end
|
||||
if (cyc < 8) begin
|
||||
if (cyc == 0 || cyc == 1 || cyc == 5 || cyc == 6) partition_cov.sample(3'(cyc));
|
||||
if (cyc == 0 || cyc == 1 || cyc >= 5) explicit_cov.sample(3'(cyc));
|
||||
cross_cov.sample(cyc < 4 ? 4'd4 : 4'd8, 2'(cyc), 1'(cyc));
|
||||
end
|
||||
if (cyc == 8) cross_cov.sample(8, 2, 1);
|
||||
if (cyc < 16) pattern_carry_cov.sample(4'(cyc), 4'(cyc), 1'(cyc / 2));
|
||||
if (cyc < 65) begin
|
||||
wide_cross_cov.sample(7'(cyc), 0, 1);
|
||||
wide_cross_cov.sample(7'(cyc), 1, 1);
|
||||
end
|
||||
else begin
|
||||
wide_cross_cov.sample(1, 0, 0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
++cyc;
|
||||
end
|
||||
endmodule
|
||||
+24
@@ -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
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = 't/t_covergroup_auto_exclusions.v'
|
||||
test.golden_filename = 't/t_covergroup_auto_exclusions.out'
|
||||
|
||||
if test.tsan:
|
||||
test.skip("ThreadSanitizer not compatible with AddressSanitizer\n")
|
||||
|
||||
# Construction-time value analysis and dynamic cross layouts under AddressSanitizer
|
||||
coverage_covergroup_common.run(
|
||||
test, verilator_flags2=['--timing', '-CFLAGS -fsanitize=address -LDFLAGS -fsanitize=address'])
|
||||
|
||||
test.passes()
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/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_auto_exclusions.v'
|
||||
|
||||
test.compile(verilator_flags2=['--timing'], threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
@@ -11,14 +11,6 @@
|
||||
: ... note: In instance 't'
|
||||
32 | bins auto[1001];
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:44:26: Non-constant expression in bin value list; values must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
44 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:45:32: Non-constant expression in bin range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
45 | ignore_bins ign_range = {[0:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:39:12: Non-constant expression in array bins range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
39 | bins b[] = {[size_var:size_var]};
|
||||
@@ -43,16 +35,16 @@
|
||||
: ... note: In instance 't'
|
||||
44 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:45:32: Non-constant expression in bin range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
45 | ignore_bins ign_range = {[0:size_var]};
|
||||
| ^
|
||||
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:52:25: Ignoring unsupported: non-constant 'option.at_least'; using default value
|
||||
: ... note: In instance 't'
|
||||
52 | option.at_least = size_var;
|
||||
| ^~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: t/t_covergroup_autobins_bad.v:62:31: Non-constant expression in bin range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
62 | ignore_bins ign_nclo = {[size_var:4]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:59:20: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
|
||||
: ... note: In instance 't'
|
||||
59 | bins b_xz = {[4'bxxxx:4'hF]};
|
||||
@@ -65,6 +57,10 @@
|
||||
: ... note: In instance 't'
|
||||
61 | ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:62:31: Non-constant expression in bin range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
62 | ignore_bins ign_nclo = {[size_var:4]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:63:23: Non-constant expression in bin range; range bounds must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
63 | bins b_nc_ub = {[size_var:$]};
|
||||
@@ -185,20 +181,12 @@
|
||||
: ... note: In instance 't'
|
||||
161 | auto_only: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f;
|
||||
| ^~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:167:34: Non-constant expression in bin value list; values must be constants (IEEE 1800-2023 19.5)
|
||||
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:172:9: Unsupported: cross coverage with more than 2^32-1 tuples.
|
||||
: ... note: In instance 't'
|
||||
172 | xc: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f;
|
||||
| ^~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:178:34: Non-constant expression in bin range; values must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
167 | ignore_bins nonconstant = {size_var};
|
||||
178 | ignore_bins nonconstant = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:167:34: Non-constant expression in bin range; values must be constants (IEEE 1800-2023 19.5)
|
||||
: ... note: In instance 't'
|
||||
167 | ignore_bins nonconstant = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:171:23: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
|
||||
: ... note: In instance 't'
|
||||
171 | bins selected = binsof(cp_a) intersect {0};
|
||||
| ^~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:210:23: Unsupported: 'intersect' exclusion matching exceeds the selection work limit.
|
||||
: ... note: In instance 't'
|
||||
210 | bins selected = binsof(cp_a.whole) intersect {[0:30'h3fffffff]} && binsof(cp_b);
|
||||
| ^~~~~~
|
||||
%Error: Exiting due to
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ module t;
|
||||
bins b_range2 = {[0:size_var]}; // non-constant regular bin range (rhs non-const)
|
||||
bins b2 = {size_var}; // non-constant simple bin value
|
||||
ignore_bins ign = {size_var}; // non-constant ignore_bins value
|
||||
ignore_bins ign_range = {[0:size_var]}; // non-constant ignore_bins range (rhs non-const)
|
||||
ignore_bins ign_range = {[0:size_var]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
@@ -59,7 +59,7 @@ module t;
|
||||
bins b_xz = {[4'bxxxx:4'hF]}; // four-state lower bound (match-code path)
|
||||
ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]}; // four-state lower bound (range-enum path)
|
||||
ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]}; // four-state upper bound (range-enum path)
|
||||
ignore_bins ign_nclo = {[size_var:4]}; // non-constant lower bound
|
||||
ignore_bins ign_nclo = {[size_var:4]};
|
||||
bins b_nc_ub = {[size_var:$]}; // non-constant lower bound, open-ended '$' upper
|
||||
bins b_xz_ub = {[4'bxxxx:$]}; // four-state lower bound, open-ended '$' upper
|
||||
bins b_xz_arr[] = {[4'bxxxx:4'hF]}; // four-state lower bound (array-bins path)
|
||||
@@ -161,6 +161,17 @@ module t;
|
||||
auto_only: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f;
|
||||
endgroup
|
||||
|
||||
// Live bins only shrink a runtime cross, so its declared product is checked the same way.
|
||||
covergroup cgx_dynamic_large;
|
||||
cp_a: coverpoint cp_wide {ignore_bins removed = {0};}
|
||||
cp_b: coverpoint cp_wide;
|
||||
cp_c: coverpoint cp_wide;
|
||||
cp_d: coverpoint cp_wide;
|
||||
cp_e: coverpoint cp_wide;
|
||||
cp_f: coverpoint cp_wide;
|
||||
xc: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f;
|
||||
endgroup
|
||||
|
||||
covergroup cgx_binsof_excluded;
|
||||
cp_a: coverpoint cp_expr {
|
||||
bins normal = {0};
|
||||
@@ -172,45 +183,6 @@ module t;
|
||||
}
|
||||
endgroup
|
||||
|
||||
logic [29:0] complex_value;
|
||||
localparam logic [29:0] ANY = 30'bx;
|
||||
|
||||
covergroup cgx_binsof_complex;
|
||||
cp_a: coverpoint complex_value {
|
||||
bins whole = {[0:30'h3fffffff]};
|
||||
// Six pigeons in five holes: a deliberately hard union of excluded assignments.
|
||||
wildcard ignore_bins no_hole = {
|
||||
(ANY & ~30'h0000001f), (ANY & ~30'h000003e0), (ANY & ~30'h00007c00),
|
||||
(ANY & ~30'h000f8000), (ANY & ~30'h01f00000), (ANY & ~30'h3e000000)
|
||||
};
|
||||
wildcard ignore_bins shared_hole = {
|
||||
(ANY | 30'h00000021), (ANY | 30'h00000401), (ANY | 30'h00008001), (ANY | 30'h00100001),
|
||||
(ANY | 30'h02000001), (ANY | 30'h00000420), (ANY | 30'h00008020), (ANY | 30'h00100020),
|
||||
(ANY | 30'h02000020), (ANY | 30'h00008400), (ANY | 30'h00100400), (ANY | 30'h02000400),
|
||||
(ANY | 30'h00108000), (ANY | 30'h02008000), (ANY | 30'h02100000), (ANY | 30'h00000042),
|
||||
(ANY | 30'h00000802), (ANY | 30'h00010002), (ANY | 30'h00200002), (ANY | 30'h04000002),
|
||||
(ANY | 30'h00000840), (ANY | 30'h00010040), (ANY | 30'h00200040), (ANY | 30'h04000040),
|
||||
(ANY | 30'h00010800), (ANY | 30'h00200800), (ANY | 30'h04000800), (ANY | 30'h00210000),
|
||||
(ANY | 30'h04010000), (ANY | 30'h04200000), (ANY | 30'h00000084), (ANY | 30'h00001004),
|
||||
(ANY | 30'h00020004), (ANY | 30'h00400004), (ANY | 30'h08000004), (ANY | 30'h00001080),
|
||||
(ANY | 30'h00020080), (ANY | 30'h00400080), (ANY | 30'h08000080), (ANY | 30'h00021000),
|
||||
(ANY | 30'h00401000), (ANY | 30'h08001000), (ANY | 30'h00420000), (ANY | 30'h08020000),
|
||||
(ANY | 30'h08400000), (ANY | 30'h00000108), (ANY | 30'h00002008), (ANY | 30'h00040008),
|
||||
(ANY | 30'h00800008), (ANY | 30'h10000008), (ANY | 30'h00002100), (ANY | 30'h00040100),
|
||||
(ANY | 30'h00800100), (ANY | 30'h10000100), (ANY | 30'h00042000), (ANY | 30'h00802000),
|
||||
(ANY | 30'h10002000), (ANY | 30'h00840000), (ANY | 30'h10040000), (ANY | 30'h10800000),
|
||||
(ANY | 30'h00000210), (ANY | 30'h00004010), (ANY | 30'h00080010), (ANY | 30'h01000010),
|
||||
(ANY | 30'h20000010), (ANY | 30'h00004200), (ANY | 30'h00080200), (ANY | 30'h01000200),
|
||||
(ANY | 30'h20000200), (ANY | 30'h00084000), (ANY | 30'h01004000), (ANY | 30'h20004000),
|
||||
(ANY | 30'h01080000), (ANY | 30'h20080000), (ANY | 30'h21000000)
|
||||
};
|
||||
}
|
||||
cp_b: coverpoint cp_expr;
|
||||
xc: cross cp_a, cp_b {
|
||||
bins selected = binsof(cp_a.whole) intersect {[0:30'h3fffffff]} && binsof(cp_b);
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cgx_binsof_many_values;
|
||||
cp_a: coverpoint cp_wide {
|
||||
bins whole[] = {[0:767]};
|
||||
@@ -255,8 +227,8 @@ module t;
|
||||
cgx_arr_open cgx_arr_open_inst = new;
|
||||
cgx_binsof cgx_binsof_inst = new;
|
||||
cgx_binsof_large cgx_binsof_large_inst = new;
|
||||
cgx_dynamic_large cgx_dynamic_large_inst = new;
|
||||
cgx_binsof_excluded cgx_binsof_excluded_inst = new;
|
||||
cgx_binsof_complex cgx_binsof_complex_inst = new;
|
||||
cgx_binsof_many_values cgx_binsof_many_values_inst = new;
|
||||
|
||||
initial $finish;
|
||||
|
||||
@@ -16,7 +16,7 @@ cg_binsof.cp_a.arrayed[1]: 2
|
||||
cg_binsof.cp_a.high: 2
|
||||
cg_binsof.cp_a.ignored [ignore]: 2
|
||||
cg_binsof.cp_a.low: 4
|
||||
cg_binsof.cp_a.rest [default]: 8
|
||||
cg_binsof.cp_a.rest [default]: 6
|
||||
cg_binsof.cp_b.either: 18
|
||||
cg_binsof.cp_b.high: 9
|
||||
cg_binsof.cp_b.low: 9
|
||||
|
||||
@@ -11,7 +11,6 @@ cg_excluded_four_state.cp_a.states: 0
|
||||
cg_excluded_four_state.cp_a.xstate [ignore]: 0
|
||||
cg_excluded_four_state.cp_b.auto_0: 0
|
||||
cg_excluded_four_state.cp_b.auto_1: 0
|
||||
cg_excluded_four_state.selected.kept [cross]: 0
|
||||
cg_excluded_four_state.selected.numeric [cross]: 0
|
||||
cg_excluded_many.cp_a.whole: 2
|
||||
cg_excluded_many.cp_a.zero_bit [ignore]: 0
|
||||
@@ -103,12 +102,8 @@ cg_fixed_words.selected.all_values [cross]: 16
|
||||
cg_fixed_words.sparse.all_values [cross]: 16
|
||||
cg_fixed_words.sparse.subset [cross]: 4
|
||||
cg_four_state.cp_a.known: 0
|
||||
cg_four_state.cp_a.xstate: 0
|
||||
cg_four_state.cp_a.zstate: 0
|
||||
cg_four_state.cp_b.auto_0: 0
|
||||
cg_four_state.cp_b.auto_1: 0
|
||||
cg_four_state.selected.exact_x [cross]: 0
|
||||
cg_four_state.selected.exact_z [cross]: 0
|
||||
cg_four_state.selected.not_x [cross]: 0
|
||||
cg_guards.cp_a.zero: 8
|
||||
cg_guards.cp_b.zero: 8
|
||||
|
||||
@@ -303,7 +303,8 @@ module t;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Check four-state bin identities without relying on four-state sampling.
|
||||
// Values with x or z bits do not participate in bins or selections (IEEE 1800-2023 19.5.7),
|
||||
// so the x/z bins and the selections of x/z values have no values and are not reported.
|
||||
covergroup cg_four_state with function sample (logic [2:0] a, bit b);
|
||||
cp_a: coverpoint a {
|
||||
bins known = {3'b001};
|
||||
|
||||
@@ -57,9 +57,6 @@ cg5.cp_addr.addr0: 1
|
||||
cg5.cp_addr.addr1: 1
|
||||
cg5.cp_cmd.read: 1
|
||||
cg5.cp_cmd.write: 1
|
||||
cg_arr_4state.a4.av[0]_x_read [cross]: 1
|
||||
cg_arr_4state.a4.av[0]_x_write [cross]: 1
|
||||
cg_arr_4state.cp_addr.av[0]: 2
|
||||
cg_arr_4state.cp_cmd.read: 1
|
||||
cg_arr_4state.cp_cmd.write: 1
|
||||
cg_arr_range.ar.av[0]_x_read [cross]: 1
|
||||
@@ -128,11 +125,8 @@ cg_ignore.cross_ab.a0_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a0_x_write [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_write [cross]: 1
|
||||
cg_inv.cp_addr.inv: 0
|
||||
cg_inv.cp_cmd.read: 1
|
||||
cg_inv.cp_cmd.write: 1
|
||||
cg_inv.iv.inv_x_read [cross]: 0
|
||||
cg_inv.iv.inv_x_write [cross]: 0
|
||||
cg_mixed.ab.addr0_x_read [cross]: 1
|
||||
cg_mixed.ab.addr0_x_write [cross]: 1
|
||||
cg_mixed.ab.addr1_x_read [cross]: 1
|
||||
|
||||
@@ -171,7 +171,8 @@ module t;
|
||||
// Crossed coverpoint with a four-state literal in a non-wildcard array bin
|
||||
// (bins av[] = {2'b0x}): LRM 1800-2023 19.5.4 permits 4-state values in a bin definition.
|
||||
// The hit-list sizing cannot statically analyze a 4-state value, so it falls back to the
|
||||
// safe slot count. Under Verilator's 2-state simulation the value matches addr=0.
|
||||
// safe slot count. A value with x or z bits does not participate (IEEE 1800-2023 19.5.7),
|
||||
// so av[0] and its cross bins have no values and leave the coverage computation.
|
||||
covergroup cg_arr_4state;
|
||||
cp_addr: coverpoint addr {bins av[] = {2'b0x};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
@@ -204,9 +205,10 @@ module t;
|
||||
orc: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the bin matches no
|
||||
// value, so the hit-list sizing rejects it (lo > hi) and falls back to the safe slot count.
|
||||
// The 'inv' bin and its cross bins are therefore never hit (coverage stays at 40%).
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the range is empty
|
||||
// (IEEE 1800-2023 11.4.13), so the hit-list sizing rejects it (lo > hi) and falls back to the
|
||||
// safe slot count. A bin without values, and its cross bins, leave the coverage computation
|
||||
// (IEEE 1800-2023 19.11.1).
|
||||
covergroup cg_inv;
|
||||
cp_addr: coverpoint addr {bins inv = {[3 : 0]};} // inverted -> never matches
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
@@ -486,11 +488,11 @@ module t;
|
||||
addr = 0; cmd = 1; cg_wild_solo_inst.sample();
|
||||
`checkr(cg_wild_solo_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} matches addr=0 (2-state sim); cross 1x2
|
||||
// cg_arr_4state: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} has no value, so addr=0 hits no bin
|
||||
// cg_arr_4state: 0+2+0=2 bins; sample both cmd values -> 100%
|
||||
addr = 0; cmd = 0; cg_arr_4state_inst.sample();
|
||||
addr = 0; cmd = 1; cg_arr_4state_inst.sample();
|
||||
`checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
`checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 2/2
|
||||
|
||||
// Sample cg_overlap: overlapping range bins lo={0,1}, hi={1,2}; cross 2x2
|
||||
// cg_overlap: 2+2+4=8 bins; cover lo/hi via addr 0 and 2, plus addr=1 double-hits both
|
||||
@@ -517,11 +519,11 @@ module t;
|
||||
addr = 2; cmd = 1; cg_openrange_inst.sample(); // hi x write
|
||||
`checkr(cg_openrange_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_inv: inverted range bin never matches; only cmd bins are hittable
|
||||
// cg_inv: 1+2+2=5 bins; inv and its 2 cross bins never hit -> 2/5=40%
|
||||
// Sample cg_inv: the empty inverted range bin and its cross bins are not counted
|
||||
// cg_inv: 0+2+0=2 bins -> read + write give 2/2=100%
|
||||
addr = 0; cmd = 0; cg_inv_inst.sample(); // read
|
||||
addr = 1; cmd = 1; cg_inv_inst.sample(); // write
|
||||
`checkr(cg_inv_inst.get_inst_coverage(), 40.0); // 2/5: read + write only
|
||||
`checkr(cg_inv_inst.get_inst_coverage(), 100.0); // 2/2: read + write only
|
||||
|
||||
// Sample cg_noNormal: coverpoint has no Normal bins; cross product is empty
|
||||
// cg_noNormal: 0+2+0=2 bins (cmd only); both hit -> 100%
|
||||
|
||||
@@ -19,4 +19,34 @@
|
||||
: ... note: In instance 't'
|
||||
19 | bins invalid_right = cx || other;
|
||||
| ^~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:33:26: Cross selection 'other' may only name its enclosing cross 'cx' (IEEE 1800-2023 19.6.1.2).
|
||||
: ... note: In instance 't'
|
||||
33 | bins wrong_cross = other;
|
||||
| ^~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:34:27: Cross selection 'missing' may only name its enclosing cross 'cx' (IEEE 1800-2023 19.6.1.2).
|
||||
: ... note: In instance 't'
|
||||
34 | bins invalid_left = missing && cx;
|
||||
| ^~~~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:35:34: Cross selection 'missing' may only name its enclosing cross 'cx' (IEEE 1800-2023 19.6.1.2).
|
||||
: ... note: In instance 't'
|
||||
35 | bins invalid_right = cx || missing;
|
||||
| ^~~~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:36:26: binsof coverpoint 'cp_other' is not an item of cross 'cx' (IEEE 1800-2023 19.6.1).
|
||||
: ... note: In instance 't'
|
||||
36 | bins wrong_point = binsof (cp_other);
|
||||
| ^~~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:37:24: Cannot find bin 'missing' in coverpoint 'cp_a' (IEEE 1800-2023 19.6.1).
|
||||
: ... note: In instance 't'
|
||||
37 | bins wrong_bin = binsof (cp_a.missing);
|
||||
| ^~~~~~
|
||||
%Error: t/t_covergroup_cross_ref_bad.v:39:7: Duplicate cross bin 'duplicate' (IEEE 1800-2023 19.6.1).
|
||||
: ... note: In instance 't'
|
||||
39 | bins duplicate = binsof (cp_b);
|
||||
| ^~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_cross_ref_bad.v:40:26: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
|
||||
: ... note: In instance 't'
|
||||
40 | bins nonconstant = binsof (cp_a) intersect {limit_value};
|
||||
| ^~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.scenarios('linter')
|
||||
|
||||
test.compile(verilator_flags2=['--coverage'], fails=True, expect_filename=test.golden_filename)
|
||||
test.lint(verilator_flags2=['--coverage'], fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
||||
@@ -20,7 +20,29 @@ module t;
|
||||
}
|
||||
endgroup
|
||||
|
||||
int limit_value;
|
||||
|
||||
covergroup cg_dynamic;
|
||||
cp_a: coverpoint value {
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
cp_b: coverpoint value;
|
||||
cp_other: coverpoint value;
|
||||
other: cross cp_a, cp_b;
|
||||
cx: cross cp_a, cp_b{
|
||||
bins wrong_cross = other;
|
||||
bins invalid_left = missing && cx;
|
||||
bins invalid_right = cx || missing;
|
||||
bins wrong_point = binsof (cp_other);
|
||||
bins wrong_bin = binsof (cp_a.missing);
|
||||
bins duplicate = cx;
|
||||
bins duplicate = binsof (cp_b);
|
||||
bins nonconstant = binsof (cp_a) intersect {limit_value};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cov = new;
|
||||
cg_dynamic dynamic_cov = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
||||
@@ -5,10 +5,10 @@ cg2.cp_only_default.all [default]: 4
|
||||
cg3.data.bad [ignore]: 1
|
||||
cg3.data.err [illegal]: 0
|
||||
cg3.data.normal: 2
|
||||
cg3.data.other [default]: 2
|
||||
cg3.data.other [default]: 1
|
||||
cg4.cp_idx.auto_0: 1
|
||||
cg4.cp_idx.auto_1: 1
|
||||
cg4.cp_idx.auto_2: 1
|
||||
cg4.cp_idx.auto_3: 1
|
||||
cg4.cp_idx.skip [ignore]: 0
|
||||
cg5.cp_data64.auto[0]: 2
|
||||
cg5.cp_data64.auto[1]: 0
|
||||
|
||||
@@ -59,7 +59,7 @@ module t;
|
||||
endgroup
|
||||
|
||||
// Auto-bins on a small range with one value excluded by ignore_bins -
|
||||
// when the range is small enough, one auto-bin per valid value is created; the excluded value is skipped.
|
||||
// the empty auto_2 bin is omitted without renumbering the remaining bins.
|
||||
covergroup cg4;
|
||||
cp_idx: coverpoint idx {
|
||||
ignore_bins skip = {2}; // value 2 excluded; auto-bins created for 0,1,3
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/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_auto_exclusions.v'
|
||||
|
||||
test.compile(verilator_flags2=[
|
||||
'--coverage-user', '--protect-ids', '--protect-key AUTO_EXCLUSION_KEY', '-Wno-INSECURE'
|
||||
],
|
||||
threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep_not(
|
||||
test.coverage_filename,
|
||||
r'cg_partition|cg_explicit|cg_projection|cg_wide_cross|t_covergroup_auto_exclusions|enabled_bin'
|
||||
)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,24 @@
|
||||
%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:14:20: Unsupported: non-integral value in a coverage bin of an integral coverpoint.
|
||||
: ... note: In instance 't'
|
||||
14 | bins text = {TEXT};
|
||||
| ^~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:21:29: Unsupported: non-integral value in a coverage bin of an integral coverpoint.
|
||||
: ... note: In instance 't'
|
||||
21 | wildcard bins text = {TEXT};
|
||||
| ^~~~
|
||||
%Error-UNSUPPORTED: t/t_covergroup_exclusions_unsup.v:27:25: Unsupported: non-integral value in a transition bin of a coverpoint with exclusions.
|
||||
: ... note: In instance 't'
|
||||
27 | bins text = (1 => TEXT);
|
||||
| ^~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_exclusions_unsup.v:40:59: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
|
||||
: ... note: In instance 't'
|
||||
40 | static_cross: cross cp_real, cp_plain{bins selected = binsof (cp_real) intersect {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_exclusions_unsup.v:41:62: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
|
||||
: ... note: In instance 't'
|
||||
41 | dynamic_cross: cross cp_real, cp_dynamic{bins selected = binsof (cp_real) intersect {1};}
|
||||
| ^~~~~~
|
||||
%Error: Exiting due to
|
||||
+16
@@ -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('vlt')
|
||||
|
||||
test.compile(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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
|
||||
|
||||
module t;
|
||||
bit [3:0] value;
|
||||
real real_value;
|
||||
localparam string TEXT = "a";
|
||||
|
||||
covergroup cg_values;
|
||||
cp: coverpoint value {
|
||||
bins text = {TEXT};
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_wild;
|
||||
cp: coverpoint value {
|
||||
wildcard bins text = {TEXT};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_transition;
|
||||
cp: coverpoint value {
|
||||
bins text = (1 => TEXT);
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_cross;
|
||||
cp_real: coverpoint real_value {
|
||||
bins one = {1.0};
|
||||
}
|
||||
cp_plain: coverpoint value;
|
||||
cp_dynamic: coverpoint value {
|
||||
ignore_bins ignored = {0};
|
||||
}
|
||||
static_cross: cross cp_real, cp_plain{bins selected = binsof (cp_real) intersect {1};}
|
||||
dynamic_cross: cross cp_real, cp_dynamic{bins selected = binsof (cp_real) intersect {1};}
|
||||
endgroup
|
||||
|
||||
cg_values values_cov = new;
|
||||
cg_wild wild_cov = new;
|
||||
cg_transition transition_cov = new;
|
||||
cg_cross cross_cov = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
@@ -17,6 +17,6 @@ cg2.cp_auto_ub.ub [ignore]: 2
|
||||
cg2.cp_bounds.hi: 2
|
||||
cg2.cp_bounds.lo: 2
|
||||
cg2.cp_full.all: 4
|
||||
cg3.cp_auto_lb.auto_0: 1
|
||||
cg3.cp_auto_lb.auto_1: 1
|
||||
cg3.cp_auto_lb.auto_2: 1
|
||||
cg3.cp_auto_lb.auto_3: 1
|
||||
cg3.cp_auto_lb.lb [ignore]: 1
|
||||
|
||||
@@ -27,7 +27,7 @@ module t;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg2: ignore_bins using a range - auto-bins are created only for values not in the range.
|
||||
// cg2: ignore_bins using a range - empty auto-bins are omitted after partitioning.
|
||||
// Also tests range-boundary conditions: when lo==0 or hi==maxVal, the range check simplifies.
|
||||
// Also tests ignore_bins with a transition list.
|
||||
covergroup cg2;
|
||||
@@ -87,10 +87,10 @@ module t;
|
||||
data2 = 0;
|
||||
cg3_inst.sample(); // lb (ignored)
|
||||
data2 = 2;
|
||||
cg3_inst.sample(); // auto_0
|
||||
cg3_inst.sample(); // auto_2
|
||||
`checkr(cg3_inst.get_inst_coverage(), 50.0);
|
||||
data2 = 3;
|
||||
cg3_inst.sample(); // auto_1
|
||||
cg3_inst.sample(); // auto_3
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
%Warning: t/t_covergroup_limits.v:42: Coverage bin exclusions exceed the decision-graph work limit; bin retained
|
||||
*-* All Finished *-*
|
||||
Executable
+18
@@ -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_all')
|
||||
|
||||
test.compile(verilator_flags2=['--timing'], threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// A bin whose exclusions exceed a search limit is retained, and a cross bin whose selection
|
||||
// exceeds one is ignored, each with a runtime warning; sampling still applies the exclusions.
|
||||
module t (
|
||||
input clk
|
||||
);
|
||||
int cyc = 0;
|
||||
|
||||
`ifdef LIMIT_DEPTH
|
||||
localparam bit [1024:0] VALUE = 1;
|
||||
localparam real EXCLUDED_COVERAGE = 100.0 * 1.0 / 3.0;
|
||||
covergroup cg with function sample (bit [1024:0] value, bit side);
|
||||
cp: coverpoint value {
|
||||
bins whole = {[$ : $]};
|
||||
ignore_bins endpoints = {1025'b0, {1025{1'b1}}};
|
||||
}
|
||||
other: coverpoint side {
|
||||
bins zero = {0};
|
||||
}
|
||||
cx: cross cp, other{bins selected = binsof (cp) intersect {[0 : $]};}
|
||||
endgroup
|
||||
`else
|
||||
localparam logic [63:0] ANY = 64'bx;
|
||||
localparam bit [63:0] VALUE = 64'h0000_0000_00ff_ffff;
|
||||
`ifdef LIMIT_QUERY
|
||||
localparam real EXCLUDED_COVERAGE = 100.0 * 1.0 / 3.0;
|
||||
`else
|
||||
localparam real EXCLUDED_COVERAGE = 50.0;
|
||||
`endif
|
||||
covergroup cg with function sample (bit [63:0] value, bit side);
|
||||
cp: coverpoint value {
|
||||
bins whole = {[$ : $]};
|
||||
wildcard ignore_bins pairs = {
|
||||
ANY & ~64'h0000000100000001, ANY & ~64'h0000000200000002,
|
||||
ANY & ~64'h0000000400000004, ANY & ~64'h0000000800000008,
|
||||
ANY & ~64'h0000001000000010, ANY & ~64'h0000002000000020,
|
||||
ANY & ~64'h0000004000000040, ANY & ~64'h0000008000000080,
|
||||
ANY & ~64'h0000010000000100, ANY & ~64'h0000020000000200,
|
||||
ANY & ~64'h0000040000000400, ANY & ~64'h0000080000000800,
|
||||
ANY & ~64'h0000100000001000, ANY & ~64'h0000200000002000,
|
||||
ANY & ~64'h0000400000004000, ANY & ~64'h0000800000008000,
|
||||
ANY & ~64'h0001000000010000, ANY & ~64'h0002000000020000,
|
||||
ANY & ~64'h0004000000040000, ANY & ~64'h0008000000080000,
|
||||
ANY & ~64'h0010000000100000, ANY & ~64'h0020000000200000,
|
||||
ANY & ~64'h0040000000400000, ANY & ~64'h0080000000800000
|
||||
};
|
||||
`ifndef LIMIT_QUERY
|
||||
ignore_bins endpoint = {64'hffffffffffffffff};
|
||||
`endif
|
||||
}
|
||||
other: coverpoint side {
|
||||
bins zero = {0};
|
||||
}
|
||||
`ifdef LIMIT_QUERY
|
||||
cx: cross cp, other{bins selected = binsof (cp) intersect {[0 : 64'hfffffffefffffffe]};}
|
||||
`endif
|
||||
endgroup
|
||||
`endif
|
||||
|
||||
cg cov;
|
||||
|
||||
always @(posedge clk) begin
|
||||
++cyc;
|
||||
if (cyc == 3) cov = new;
|
||||
if (cyc == 4) begin
|
||||
cov.sample(0, 0); // Excluded
|
||||
`checkr(cov.get_inst_coverage(), EXCLUDED_COVERAGE);
|
||||
cov.sample(VALUE, 0);
|
||||
`checkr(cov.get_inst_coverage(), 100.0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,3 @@
|
||||
%Warning: t/t_covergroup_limits.v:24: Coverage bin exclusions exceed the decision-graph depth limit; bin retained
|
||||
%Warning: t/t_covergroup_limits.v:30: Cross bin selection exceeds the decision-graph depth limit; bin ignored
|
||||
*-* All Finished *-*
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/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_limits.v'
|
||||
|
||||
test.compile(verilator_flags2=['--timing', '+define+LIMIT_DEPTH'],
|
||||
threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,2 @@
|
||||
%Warning: t/t_covergroup_limits.v:65: Cross bin selection exceeds the decision-graph work limit; bin ignored
|
||||
*-* All Finished *-*
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/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_limits.v'
|
||||
|
||||
test.compile(verilator_flags2=['--timing', '+define+LIMIT_QUERY'],
|
||||
threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,23 @@
|
||||
%Error: t/t_covergroup_wild_real_bad.v:11:9: Cannot use a wildcard bin on a coverpoint of type 'real' (IEEE 1800-2023 19.5.4).
|
||||
: ... note: In instance 't'
|
||||
11 | cp: coverpoint value {
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_wild_real_bad.v:12:21: ... Location of wildcard bin
|
||||
12 | wildcard bins one = {1};
|
||||
| ^~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_covergroup_wild_real_bad.v:14:15: Cannot use a wildcard bin on a coverpoint of type 'real' (IEEE 1800-2023 19.5.4).
|
||||
: ... note: In instance 't'
|
||||
14 | cp_range: coverpoint value {
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_wild_real_bad.v:15:21: ... Location of wildcard bin
|
||||
15 | wildcard bins range = {[1 : 2]};
|
||||
| ^~~~~
|
||||
%Error: t/t_covergroup_wild_real_bad.v:17:14: Cannot use a wildcard bin on a coverpoint of type 'real' (IEEE 1800-2023 19.5.4).
|
||||
: ... note: In instance 't'
|
||||
17 | cp_open: coverpoint value {
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_wild_real_bad.v:18:21: ... Location of wildcard bin
|
||||
18 | wildcard bins open = {[1 : $]};
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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
|
||||
|
||||
module t;
|
||||
real value;
|
||||
|
||||
covergroup cg;
|
||||
cp: coverpoint value {
|
||||
wildcard bins one = {1};
|
||||
}
|
||||
cp_range: coverpoint value {
|
||||
wildcard bins range = {[1 : 2]};
|
||||
}
|
||||
cp_open: coverpoint value {
|
||||
wildcard bins open = {[1 : $]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cov = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
@@ -1023,6 +1023,59 @@ module Vt_debug_emitv_t;
|
||||
input string name;
|
||||
endfunction
|
||||
endcovergroup
|
||||
covergroup Vt_debug_emitv_cg_live_cross;
|
||||
function new;
|
||||
cp_x: coverpoint cg_sig {
|
||||
ignore_bins removed = {'sh0};
|
||||
};
|
||||
cp_y: coverpoint cg_sig2;
|
||||
cx: cross cp_x, cp_y {
|
||||
bins all = cx;
|
||||
}
|
||||
endfunction
|
||||
int signed __Vint;
|
||||
struct {
|
||||
string name;
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
int signed at_least;
|
||||
int signed auto_bin_max;
|
||||
int signed cross_num_print_missing;
|
||||
bit cross_retain_auto_bins;
|
||||
bit detect_overlap;
|
||||
bit per_instance;
|
||||
bit get_inst_coverage;
|
||||
} option;
|
||||
struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
bit strobe;
|
||||
bit merge_instances;
|
||||
bit distribute_first;
|
||||
real real_interval;
|
||||
} type_option;
|
||||
function sample;
|
||||
endfunction
|
||||
function start;
|
||||
endfunction
|
||||
function stop;
|
||||
endfunction
|
||||
function get_coverage;
|
||||
get_coverage = /*CRESET*/;
|
||||
input string covered_bins;
|
||||
input string total_bins;
|
||||
endfunction
|
||||
function get_inst_coverage;
|
||||
get_inst_coverage = /*CRESET*/;
|
||||
input string covered_bins;
|
||||
input string total_bins;
|
||||
endfunction
|
||||
function set_inst_name;
|
||||
input string name;
|
||||
endfunction
|
||||
endcovergroup
|
||||
Vt_debug_emitv_cg_basic cg_basic_instVt_debug_emitv_cg_basic;
|
||||
cg_basic_inst = new();
|
||||
Vt_debug_emitv_cg_clocked cg_clocked_instVt_debug_emitv_cg_clocked;
|
||||
@@ -1031,6 +1084,8 @@ module Vt_debug_emitv_t;
|
||||
cg_trans_inst = new();
|
||||
Vt_debug_emitv_cg_cross cg_cross_instVt_debug_emitv_cg_cross;
|
||||
cg_cross_inst = new();
|
||||
Vt_debug_emitv_cg_live_cross cg_live_cross_instVt_debug_emitv_cg_live_cross;
|
||||
cg_live_cross_inst = new();
|
||||
enum logic [2:0] {
|
||||
ZERO = 3'h0,
|
||||
ONE = 3'h1
|
||||
|
||||
@@ -452,10 +452,22 @@ module t (/*AUTOARG*/
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_live_cross;
|
||||
option.auto_bin_max = 4;
|
||||
cp_x: coverpoint cg_sig {
|
||||
ignore_bins removed = {0};
|
||||
}
|
||||
cp_y: coverpoint cg_sig2;
|
||||
cx: cross cp_x, cp_y{
|
||||
bins all = cx;
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg_basic cg_basic_inst = new;
|
||||
cg_clocked cg_clocked_inst = new;
|
||||
cg_trans cg_trans_inst = new;
|
||||
cg_cross cg_cross_inst = new;
|
||||
cg_live_cross cg_live_cross_inst = new;
|
||||
endmodule
|
||||
|
||||
module sub(input logic clk);
|
||||
|
||||
@@ -570,18 +570,14 @@
|
||||
// Crossed coverpoint with a four-state literal in a non-wildcard array bin
|
||||
// (bins av[] = {2'b0x}): LRM 1800-2023 19.5.4 permits 4-state values in a bin definition.
|
||||
// The hit-list sizing cannot statically analyze a 4-state value, so it falls back to the
|
||||
// safe slot count. Under Verilator's 2-state simulation the value matches addr=0.
|
||||
// safe slot count. A value with x or z bits does not participate (IEEE 1800-2023 19.5.7),
|
||||
// so av[0] and its cross bins have no values and leave the coverage computation.
|
||||
covergroup cg_arr_4state;
|
||||
%000002 cp_addr: coverpoint addr {bins av[] = {2'b0x};}
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_4state.cp_addr.av[0]
|
||||
cp_addr: coverpoint addr {bins av[] = {2'b0x};}
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.cp_cmd.write
|
||||
%000001 a4: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.a4.av[0]_x_read
|
||||
// cross: [av[0], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.a4.av[0]_x_write
|
||||
// cross: [av[0], write]
|
||||
a4: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with two *overlapping* Normal range bins (addr=1 is in both lo and
|
||||
@@ -646,20 +642,16 @@
|
||||
// cross: [lo, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the bin matches no
|
||||
// value, so the hit-list sizing rejects it (lo > hi) and falls back to the safe slot count.
|
||||
// The 'inv' bin and its cross bins are therefore never hit (coverage stays at 40%).
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the range is empty
|
||||
// (IEEE 1800-2023 11.4.13), so the hit-list sizing rejects it (lo > hi) and falls back to the
|
||||
// safe slot count. A bin without values, and its cross bins, leave the coverage computation
|
||||
// (IEEE 1800-2023 19.11.1).
|
||||
covergroup cg_inv;
|
||||
%000000 cp_addr: coverpoint addr {bins inv = {[3 : 0]};} // inverted -> never matches
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.cp_addr.inv
|
||||
cp_addr: coverpoint addr {bins inv = {[3 : 0]};} // inverted -> never matches
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_inv.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_inv.cp_cmd.write
|
||||
%000000 iv: cross cp_addr, cp_cmd;
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.iv.inv_x_read
|
||||
// cross: [inv, read]
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.iv.inv_x_write
|
||||
// cross: [inv, write]
|
||||
iv: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with *no* Normal bins (only ignore_bins): the cross has an empty bin
|
||||
@@ -1214,13 +1206,13 @@
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} matches addr=0 (2-state sim); cross 1x2
|
||||
// cg_arr_4state: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} has no value, so addr=0 hits no bin
|
||||
// cg_arr_4state: 0+2+0=2 bins; sample both cmd values -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_arr_4state_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_arr_4state_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
%000001 `checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 2/2
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
@@ -1272,13 +1264,13 @@
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_inv: inverted range bin never matches; only cmd bins are hittable
|
||||
// cg_inv: 1+2+2=5 bins; inv and its 2 cross bins never hit -> 2/5=40%
|
||||
// Sample cg_inv: the empty inverted range bin and its cross bins are not counted
|
||||
// cg_inv: 0+2+0=2 bins -> read + write give 2/2=100%
|
||||
%000001 addr = 0; cmd = 0; cg_inv_inst.sample(); // read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 1; cg_inv_inst.sample(); // write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_inv_inst.get_inst_coverage(), 40.0); // 2/5: read + write only
|
||||
%000001 `checkr(cg_inv_inst.get_inst_coverage(), 100.0); // 2/2: read + write only
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
Reference in New Issue
Block a user