Support logic and set operations on binsof (#8306)

This commit is contained in:
Marco Bartoli
2026-09-13 12:59:10 -04:00
committed by GitHub
parent 298922d887
commit 47ead4e89d
26 changed files with 2455 additions and 312 deletions
+4
View File
@@ -592,6 +592,10 @@ List Of Warnings
``covergroup``, ``coverpoint``, and coverage options, and the
construct was ignored.
This includes crosses whose normal-bin Cartesian product exceeds
``2**32 - 1`` tuples. The limit is checked during verilation for both
automatic and explicit cross bins.
Disabling the :option:`UNSUPPORTED` error also disables this warning.
Ignoring this warning may make Verilator ignore lint checking on the
+199 -46
View File
@@ -115,94 +115,245 @@ void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cp
m_file = file;
m_line = line;
m_col = col;
m_dims = dims;
m_cps.assign(cps, cps + dims);
m_cpBinCounts.resize(dims);
assert(dims == m_dims);
// Accumulate in 64 bits so the overflow check itself cannot overflow.
uint64_t product = 1;
uint64_t product = m_numAutoBins ? 1 : 0;
for (uint32_t d = 0; d < dims; ++d) {
m_cpBinCounts[d] = cps[d]->normalBinCount();
product *= m_cpBinCounts[d];
m_dimensionsp[d] = {cps[d], nullptr, cps[d]->normalBinCount(), 1};
product *= m_dimensionsp[d].bins;
if (VL_UNLIKELY(product > UINT32_MAX)) { // LCOV_EXCL_START
VL_FATAL_MT(file, line, "", "Cross has too many auto bins to represent");
} // LCOV_EXCL_STOP
}
m_numAutoBins = static_cast<uint32_t>(product);
assert(product == m_numAutoBins);
// stride[d] = product of the Normal bin counts of all dimensions after d.
// Counts down with an offset so the unsigned index never wraps below zero.
m_stride.assign(dims, 1);
for (uint32_t d = dims; d > 1; --d) m_stride[d - 2] = m_stride[d - 1] * m_cpBinCounts[d - 1];
m_flatCounts.assign(m_numAutoBins, 0);
for (uint32_t d = dims; d > 1; --d) {
m_dimensionsp[d - 2].stride = m_dimensionsp[d - 1].stride * m_dimensionsp[d - 1].bins;
}
}
void VlCoverCross::addBin(uint32_t dim, uint32_t first, uint32_t bins, const char* namep,
void VlCoverCross::addBin(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.
if (m_bins.empty()) m_autoExcluded.assign(m_numAutoBins, false);
m_bins.emplace_back(dim, first, bins, namep, filep, line, col);
// Visit only selected tuples. Multiple explicit bins may select the same tuple.
const uint64_t stride = m_stride[dim];
const uint64_t period = stride * m_cpBinCounts[dim];
for (uint64_t base = first * stride; base < m_numAutoBins; base += period) {
for (uint64_t flat = base; flat < base + bins * stride; ++flat) {
m_autoExcluded[flat] = true;
}
}
Explicit& data = *m_explicitp;
const uint32_t words = m_numAutoBins / 64 + (m_numAutoBins % 64 != 0);
assert(selection.size() == words);
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);
Bin& bin = data.bins[data.numBins++];
bin.selectionp = selectionp;
bin.namep = namep;
bin.filep = filep;
bin.line = line;
bin.col = col;
uint32_t word = 0;
for (const uint64_t bits : selection) { data.wordsp[word++].autoExcluded |= bits; }
}
void VlCoverCross::finalizeBins() {
if (!hasExplicitBins()) return;
Explicit& data = *m_explicitp;
assert(data.numBins == data.bins.size());
uint32_t autoIdx = 0;
for (uint32_t flat = 0; flat < m_numAutoBins; ++flat) {
if (!m_autoExcluded[flat]) m_autoBins.push_back(flat);
if (!(data.wordsp[flat / 64].autoExcluded & (uint64_t{1} << (flat % 64)))) {
assert(autoIdx < data.autoBins.size());
data.autoBins[autoIdx++] = flat;
}
}
const uint32_t words = m_numAutoBins / 64 + (m_numAutoBins % 64 != 0);
assert(autoIdx == data.autoBins.size());
data.minBinWords = words;
uint64_t pos = 0;
const uint32_t* const indicesp = data.binWords.begin();
for (Bin& bin : data.bins) {
const uint64_t begin = pos;
for (uint32_t word = 0; word < words; ++word) {
if (bin.selectionp[word]) {
assert(pos < data.binWords.size());
data.binWords[pos++] = word;
}
}
bin.wordIndicesp = indicesp ? indicesp + begin : nullptr;
bin.numWords = static_cast<uint32_t>(pos - begin);
data.minBinWords = std::min(data.minBinWords, bin.numWords);
}
assert(pos == data.binWords.size());
}
template <bool T_Explicit, bool T_RecordHits>
void VlCoverCross::iterateProduct(uint32_t dim, uint32_t baseIdx) {
const VlCoverpoint* const cpp = m_cps[dim];
const VlCoverpoint* const cpp = m_dimensionsp[dim].cpp;
const uint32_t hits = cpp->hitCount();
const uint32_t* const list = cpp->hitList();
const uint32_t* const list = m_dimensionsp[dim].hitsp;
const bool last = (dim == m_dims - 1);
const uint32_t stride = m_stride[dim];
const uint32_t stride = m_dimensionsp[dim].stride;
for (uint32_t hit = 0; hit < hits; ++hit) {
const uint32_t idx = baseIdx + list[hit] * stride;
if (last) {
incrementTuple(idx);
if (T_Explicit) {
incrementTuple<T_RecordHits>(idx);
} else {
incrementAuto(idx);
}
} else {
iterateProduct(dim + 1, idx);
iterateProduct<T_Explicit, T_RecordHits>(dim + 1, idx);
}
}
}
template <bool T_ApplyIffs>
void VlCoverCross::sampleSingleTuple(uint32_t idx, const bool* binIffs) {
Explicit& data = *m_explicitp;
const uint32_t word = idx / 64;
const uint64_t bit = uint64_t{1} << (idx % 64);
if (!(data.wordsp[word].autoExcluded & bit)) {
incrementAuto(idx);
return;
}
for (Bin& bin : data.bins) {
if (T_ApplyIffs && !*binIffs++) continue;
if (bin.selectionp[word] & bit) {
if (bin.count++ == 0) ++m_numCovered;
}
}
}
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
void VlCoverCross::sampleBins(const bool* binIffs) {
struct HitWord final {
uint32_t index;
uint64_t bits;
};
Explicit& data = *m_explicitp;
const uint64_t bins = data.numBins;
const uint64_t touched = T_Touched ? T_Touched : data.numTouchedWords;
const Word* const wordsp = data.wordsp;
std::array<HitWord, T_Touched> cached{};
for (uint32_t i = 0; i < T_Touched; ++i) {
const uint32_t word = wordsp[i].touchedWord;
cached[i] = {word, wordsp[word].hitBits};
}
for (uint64_t binIdx = 0; binIdx < bins; ++binIdx) {
if (T_ApplyIffs && !*binIffs++) continue;
Bin& bin = data.bins[binIdx];
bool matched = false;
if (T_Touched == 1) {
matched = (bin.selectionp[cached[0].index] & cached[0].bits) != 0;
} else if (T_Dense || bin.numWords >= touched) {
for (uint64_t i = 0; i < touched; ++i) {
const uint32_t word = T_Touched ? cached[i].index : wordsp[i].touchedWord;
const uint64_t hits = T_Touched ? cached[i].bits : wordsp[word].hitBits;
if (bin.selectionp[word] & hits) {
matched = true;
break;
}
}
} else {
for (uint32_t pos = 0; pos < bin.numWords; ++pos) {
const uint32_t word = bin.wordIndicesp[pos];
if (bin.selectionp[word] & wordsp[word].hitBits) {
matched = true;
break;
}
}
}
if (matched && bin.count++ == 0) ++m_numCovered;
}
for (uint32_t i = 0; i < data.numTouchedWords; ++i) {
data.wordsp[wordsp[i].touchedWord].hitBits = 0;
}
data.numTouchedWords = 0;
}
template <bool T_ApplyIffs, bool T_Dense>
void VlCoverCross::sampleHitWords(const bool* binIffs) {
switch (m_explicitp->numTouchedWords) {
case 1: sampleBins<T_ApplyIffs, 1, T_Dense>(binIffs); break;
case 2: sampleBins<T_ApplyIffs, 2, T_Dense>(binIffs); break;
case 3: sampleBins<T_ApplyIffs, 3, T_Dense>(binIffs); break;
default: sampleBins<T_ApplyIffs, 0, T_Dense>(binIffs); break;
}
}
void VlCoverCross::sample(const bool* binIffs) {
// 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) {
if (m_cps[d]->hitCount() == 0) return;
const uint32_t hits = m_dimensionsp[d].cpp->hitCount();
if (hits == 0) return;
single &= hits == 1;
}
for (Bin& bin : m_bins) {
if (binIffs && !*binIffs++) continue;
const VlCoverpoint* const cpp = m_cps[bin.dim];
for (uint32_t hit = 0; hit < cpp->hitCount(); ++hit) {
const uint32_t idx = cpp->hitList()[hit];
if (idx >= bin.first && idx - bin.first < bin.bins) {
if (bin.count++ == 0) ++m_numCovered;
break;
if (single) {
uint32_t idx = 0;
for (uint32_t d = 0; d < m_dims; ++d) {
idx += m_dimensionsp[d].cpp->hitList()[0] * m_dimensionsp[d].stride;
}
if (hasExplicitBins()) {
if (binIffs) {
sampleSingleTuple<true>(idx, binIffs);
} else {
sampleSingleTuple<false>(idx, nullptr);
}
} else {
incrementAuto(idx);
}
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 (!enabled && m_explicitp->autoBins.empty()) return;
}
for (uint32_t d = 0; d < m_dims; ++d) {
m_dimensionsp[d].hitsp = m_dimensionsp[d].cpp->hitList();
}
if (!hasExplicitBins()) {
iterateProduct<false>(0, 0);
return;
}
if (!enabled) {
iterateProduct<true, false>(0, 0);
return;
}
iterateProduct<true>(0, 0);
if (m_explicitp->numTouchedWords) {
const bool dense = m_explicitp->minBinWords >= m_explicitp->numTouchedWords;
if (binIffs) {
if (dense) {
sampleHitWords<true, true>(binIffs);
} else {
sampleHitWords<true, false>(binIffs);
}
} else {
if (dense) {
sampleHitWords<false, true>(nullptr);
} else {
sampleHitWords<false, false>(nullptr);
}
}
}
iterateProduct(0, 0);
}
std::string VlCoverCross::binName(uint32_t i) const {
if (i < m_bins.size()) return m_bins[i].namep;
return autoBinName(autoIndex(i - static_cast<uint32_t>(m_bins.size())));
if (hasExplicitBins()) {
if (i < m_explicitp->bins.size()) return m_explicitp->bins[i].namep;
i -= static_cast<uint32_t>(m_explicitp->bins.size());
}
return autoBinName(autoIndex(i));
}
std::string VlCoverCross::autoBinName(uint32_t flat) const {
// Built on demand by concatenating each coverpoint's own bin name.
std::string name;
for (uint32_t d = 0; d < m_dims; ++d) {
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
const Dimension& dimension = m_dimensionsp[d];
const uint32_t crossIdx = (flat / dimension.stride) % dimension.bins;
if (d > 0) name += "_x_";
name += m_cps[d]->normalBinName(crossIdx);
name += dimension.cpp->normalBinName(crossIdx);
}
return name;
}
@@ -211,13 +362,14 @@ std::string VlCoverCross::autoBinName(uint32_t flat) const {
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
const std::string lineStr = std::to_string(m_line);
const std::string colStr = std::to_string(m_col);
const uint32_t explicitCount = static_cast<uint32_t>(m_bins.size());
const uint32_t explicitCount
= hasExplicitBins() ? static_cast<uint32_t>(m_explicitp->bins.size()) : 0;
// Use the same indexed names for registration and the runtime read interface.
for (uint32_t i = 0; i < binCount(); ++i) {
const std::string bin = binName(i);
const std::string full = m_hier + "." + bin;
if (i < explicitCount) {
Bin& userBin = m_bins[i];
Bin& userBin = m_explicitp->bins[i];
const std::string binLineStr = std::to_string(userBin.line);
const std::string binColStr = std::to_string(userBin.col);
VL_COVER_INSERT(covcontextp, full.c_str(), &userBin.count, "page", page, "filename",
@@ -229,11 +381,12 @@ void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* pa
// cross_bins metadata: the same components joined by ',' (not read by the report)
std::string crossBins;
for (uint32_t d = 0; d < m_dims; ++d) {
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
const Dimension& dimension = m_dimensionsp[d];
const uint32_t crossIdx = (flat / dimension.stride) % dimension.bins;
if (d > 0) crossBins += ",";
crossBins += m_cps[d]->normalBinName(crossIdx);
crossBins += dimension.cpp->normalBinName(crossIdx);
}
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCounts[flat], "page", page, "filename",
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCountsp[flat], "page", page, "filename",
m_file, "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str());
}
+151 -42
View File
@@ -35,7 +35,9 @@
#include "verilated.h"
#include "verilated_cov_model.h"
#include <array>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <string>
#include <unordered_map>
@@ -207,32 +209,63 @@ public:
// VlCoverCross
/// Per-instance cross runtime. Holds flat uint32_t[] storage over the
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
/// built on demand for automatic bins; explicit bins select a Normal-bin span
/// in one dimension and replace the corresponding automatic cross bins.
/// walks only hit tuples, not the entire product. Bin names are
/// 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
/// storage, and its borrowed storage pointers remain valid for the instance.
class VlCoverCross final : public VlCoverpointIf {
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
protected:
struct Dimension final {
VlCoverpoint* cpp; // Feeding coverpoint
const uint32_t* hitsp; // Hit list cached for Cartesian traversal
uint32_t bins; // Normal bin count
uint32_t stride; // Flat-index stride
};
struct Bin final {
const uint32_t dim; // Selected coverpoint dimension
const uint32_t first; // First selected Normal bin index
const uint32_t bins; // Number of selected Normal bins
const char* const namep; // Explicit bin name
const char* const filep; // Bin declaration file
const int line; // Bin declaration line
const int col; // Bin declaration column
const uint64_t* selectionp; // Slice of the fixed selection storage
const char* namep; // Explicit bin name
const char* filep; // Bin declaration file
int line; // Bin declaration line
int col; // Bin declaration column
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
};
struct Word final {
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
uint64_t hitBits = 0; // Selected hit tuples, cleared after each sample
uint32_t touchedWord = 0; // Flat word ID, stored by touched-list position
};
template <typename T>
class View final {
T* m_beginp;
T* m_endp;
Bin(uint32_t dim, uint32_t first, uint32_t bins, const char* namep, const char* filep,
int line, int col)
: dim{dim}
, first{first}
, bins{bins}
, namep{namep}
, filep{filep}
, line{line}
, col{col} {}
public:
View(T* datap, uint64_t size)
: m_beginp{datap}
, m_endp{datap ? datap + size : nullptr} {}
T& operator[](uint64_t i) const { return m_beginp[i]; }
uint64_t size() const { return m_beginp == m_endp ? 0 : m_endp - m_beginp; }
bool empty() const { return m_beginp == m_endp; }
T* begin() const { return m_beginp; }
T* end() const { return m_endp; }
};
struct Explicit final {
View<Bin> bins; // Explicit bins in declaration order
Word* wordsp; // Masks use flat word indices; touchedWord uses a dense prefix
View<uint32_t> autoBins; // Retained flat indices
View<uint32_t> binWords; // Nonzero selection words, grouped by bin
uint64_t* selectionp; // [bins.size() * ceil(m_numAutoBins / 64)]
uint32_t numBins = 0; // Bins configured by addBin()
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
};
private:
// MEMBERS
std::string m_hier; // "covergroup.cross"
const char* m_file = nullptr; // Cross declaration file (registration metadata)
@@ -242,37 +275,66 @@ class VlCoverCross final : public VlCoverpointIf {
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are
// built from. init() fatals if the product would exceed UINT32_MAX, so every
// index computed here provably fits. That bound is far beyond anything
// storable anyway: m_flatCounts alone would need 16GB.
// 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)
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints, set by init()
std::vector<Bin> m_bins; // Explicit bins in declaration order
std::vector<bool>
m_autoExcluded; // Tuples replaced by explicit bins; empty for auto-only crosses
std::vector<uint32_t> m_autoBins; // Retained flat indices, when explicit bins are present
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by VlCoverCrossT
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
// PRIVATE METHODS
bool hasExplicitBins() const { return m_explicitp != nullptr; }
template <bool T_Explicit, bool T_RecordHits = true>
void iterateProduct(uint32_t dim, uint32_t baseIdx);
void incrementTuple(uint32_t idx) {
if (!m_autoExcluded.empty() && m_autoExcluded[idx]) return;
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
void incrementAuto(uint32_t idx) {
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
}
template <bool T_RecordHits>
void incrementTuple(uint32_t idx) {
Explicit& data = *m_explicitp;
const uint32_t wordIdx = idx / 64;
Word& word = data.wordsp[wordIdx];
if ((word.autoExcluded >> (idx % 64)) & 1U) {
if (T_RecordHits) {
if (!word.hitBits) { data.wordsp[data.numTouchedWords++].touchedWord = wordIdx; }
word.hitBits |= uint64_t{1} << (idx % 64);
}
// Explicit selections consume automatic tuples independently of iff.
return;
}
incrementAuto(idx);
}
template <bool T_ApplyIffs>
void sampleSingleTuple(uint32_t idx, const bool* binIffs);
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
void sampleBins(const bool* binIffs);
template <bool T_ApplyIffs, bool T_Dense>
void sampleHitWords(const bool* binIffs);
uint32_t autoIndex(uint32_t i) const {
return hasExplicitBins() ? m_explicitp->autoBins[i] : i;
}
uint32_t autoIndex(uint32_t i) const { return m_bins.empty() ? i : m_autoBins[i]; }
std::string autoBinName(uint32_t flat) const;
public:
protected:
// CONSTRUCTORS
VlCoverCross() = default;
VlCoverCross(uint32_t dims, uint32_t tuples)
: m_dims{dims}
, m_numAutoBins{tuples} {}
void bindStorage(Dimension* dimensionsp, uint32_t* countsp, Explicit* explicitp = nullptr) {
m_dimensionsp = dimensionsp;
m_flatCountsp = countsp;
m_explicitp = explicitp;
}
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);
/// Add a single-binsof cross bin using verilation-time resolved Normal-bin indices.
void addBin(uint32_t dim, uint32_t first, uint32_t bins, const char* namep, const char* filep,
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples.
void addBin(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();
@@ -280,14 +342,15 @@ public:
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
/// Reads the feeding coverpoints from m_cps, so the caller passes no coverpoints.
/// Reads the feeding coverpoints saved by init(), so the caller passes no coverpoints.
void sample(const bool* binIffs = nullptr);
// ---- VlCoverpointIf ----
// Explicit bins precede retained automatic bins; all are Normal bins.
uint32_t binCount() const override {
return m_bins.empty() ? m_numAutoBins
: static_cast<uint32_t>(m_bins.size() + m_autoBins.size());
return hasExplicitBins()
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
: m_numAutoBins;
}
std::string binName(uint32_t i) const override;
void coverageParts(double& covered, double& total) const override {
@@ -296,6 +359,51 @@ public:
}
};
//=============================================================================
// VlCoverCrossT
/// Cross storage with verilation-time dimensions and bin capacities. All bin
/// data stays at the registry-owned object's address; no per-buffer allocations
/// or per-shape copies of the sampling algorithm are needed.
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
class VlCoverCrossT final : public VlCoverCross {
static constexpr uint32_t WORDS = Tuples / 64 + (Tuples % 64 != 0);
static_assert(Bins > 0, "Explicit cross storage requires bins");
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
std::array<Bin, Bins> m_bins;
std::array<Word, WORDS> m_words{};
std::array<uint32_t, AutoBins> m_autoBins;
std::array<uint32_t, BinWords> m_binWords;
std::array<uint64_t, static_cast<uint64_t>(Bins) * WORDS> m_selections;
Explicit m_explicit;
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples}
, m_explicit{{m_bins.data(), Bins},
m_words.data(),
{m_autoBins.data(), AutoBins},
{m_binWords.data(), BinWords},
m_selections.data()} {
bindStorage(m_dimensions.data(), m_counts.data(), &m_explicit);
}
};
/// Automatic-only crosses omit every explicit-bin array and its bookkeeping.
template <uint32_t Dims, uint32_t Tuples>
class VlCoverCrossT<Dims, Tuples, 0, 0, 0> final : public VlCoverCross {
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples} {
bindStorage(m_dimensions.data(), m_counts.data());
}
};
class VlCovergroupType;
//=============================================================================
@@ -340,8 +448,9 @@ public:
m_items.emplace_back(cpp);
return cpp; // borrowed by the generated class
}
VlCoverCross* addCross() {
VlCoverCross* const cxp = new VlCoverCross{};
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>* addCross() {
auto* const cxp = new VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>{};
m_items.emplace_back(cxp);
return cxp; // borrowed by the generated class
}
+1 -7
View File
@@ -510,7 +510,6 @@ public:
RANDOM_GENERATOR,
RANDOM_STDGENERATOR,
COVERGROUP_INSTHANDLE,
COVERGROUP_CROSS,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -547,7 +546,6 @@ public:
"VlRandomizer",
"VlStdRandomizer",
"VlCovInstHandle",
"VlCoverCross*",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -581,7 +579,6 @@ public:
"%E-rand-gen",
"%E-stdrand-gen",
"%E-cg-insthandle",
"%E-cover-cross",
"IData",
"QData",
"%E-logic-implct",
@@ -627,7 +624,6 @@ public:
case RANDOM_GENERATOR: return 0; // opaque
case RANDOM_STDGENERATOR: return 0; // opaque
case COVERGROUP_INSTHANDLE: return 0; // opaque
case COVERGROUP_CROSS: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -669,8 +665,7 @@ public:
|| m_e == MTASKSTATE || m_e == DELAY_SCHEDULER || m_e == TRIGGER_SCHEDULER
|| m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC || m_e == PROCESS_REFERENCE
|| m_e == RANDOM_GENERATOR || m_e == RANDOM_STDGENERATOR
|| m_e == COVERGROUP_INSTHANDLE || m_e == COVERGROUP_CROSS || m_e == DOUBLE
|| m_e == UNTYPED);
|| m_e == COVERGROUP_INSTHANDLE || m_e == DOUBLE || m_e == UNTYPED);
}
bool isCHandle() const VL_MT_SAFE { return m_e == CHANDLE; }
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
@@ -726,7 +721,6 @@ public:
/* RANDOM_GENERATOR: */ "", // Should not be traced
/* RANDOM_STD_GENERATOR: */ "", // Should not be traced
/* COVERGROUP_INSTHANDLE: */ "", // Should not be traced
/* COVERGROUP_CROSS: */ "", // Should not be traced
/* UINT32: */ "BIT",
/* UINT64: */ "BIT",
/* LOGIC_IMPLICIT: */ "", // Should not be traced
+44 -3
View File
@@ -501,9 +501,6 @@ public:
bool isCovergroupInstHandle() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::COVERGROUP_INSTHANDLE;
}
bool isCovergroupCross() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::COVERGROUP_CROSS;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -693,6 +690,50 @@ public:
int widthTotalBytes() const override { return 1; }
bool isCompound() const override { return false; }
};
class AstCoverCrossDType final : public AstNodeDType {
// Borrowed pointer to VlCoverCrossT<dimensions, tuples, bins, autoBins, binWords>.
const uint32_t m_dimensions;
const uint32_t m_tuples;
const uint32_t m_bins;
const uint32_t m_autoBins;
const uint64_t m_binWords;
public:
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
uint32_t autoBins, uint64_t binWords)
: ASTGEN_SUPER_CoverCrossDType(fl)
, m_dimensions{dimensions}
, m_tuples{tuples}
, m_bins{bins}
, m_autoBins{autoBins}
, m_binWords{binWords} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverCrossDType;
const char* broken() const override {
BROKEN_RTN(m_dimensions == 0);
return nullptr;
}
bool sameNode(const AstNode* samep) const override {
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();
}
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
uint32_t dimensions() const { return m_dimensions; }
uint32_t tuples() const { return m_tuples; }
uint32_t bins() const { return m_bins; }
uint32_t autoBins() const { return m_autoBins; }
uint64_t binWords() const { return m_binWords; }
string cppTemplateArgs() const;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return sizeof(void*); }
int widthTotalBytes() const override { return sizeof(void*); }
bool isCompound() const override { return true; }
};
class AstCoverpointDType final : public AstNodeDType {
// Borrowed pointer to a covergroup coverpoint runtime, 'VlCoverpointT<hitBound>*'.
// Follows pattern of AstQueueDType in capturing the compile-time max bin overlap
+36 -2
View File
@@ -1108,16 +1108,28 @@ public:
class AstCoverBinsof final : public AstNode {
// A binsof selection of a coverpoint or one of its named bins
// @astgen op1 := pointp : AstCoverpointRef
// @astgen op2 := rangesp : List[AstNode] // Optional intersect value ranges
string m_name; // Selected bin name, or empty for all bins of the coverpoint
const bool m_isNegated; // Complement the selection within the cross product
public:
AstCoverBinsof(FileLine* fl, AstCoverpointRef* pointp)
: ASTGEN_SUPER_CoverBinsof(fl) {
AstCoverBinsof(FileLine* fl, AstCoverpointRef* pointp, bool isNegated = false,
AstNode* rangesp = nullptr)
: ASTGEN_SUPER_CoverBinsof(fl)
, m_isNegated{isNegated} {
this->pointp(pointp);
addRangesp(rangesp);
}
ASTGEN_MEMBERS_AstCoverBinsof;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isNegated() const { return m_isNegated; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverBinsof* const asamep = VN_DBG_AS(samep, CoverBinsof);
return m_name == asamep->m_name && m_isNegated == asamep->m_isNegated;
} // LCOV_EXCL_STOP
};
class AstCoverCrossBin final : public AstNode {
// A named cross bin and its selection expression
@@ -1135,6 +1147,28 @@ public:
ASTGEN_MEMBERS_AstCoverCrossBin;
string name() const override VL_MT_STABLE { return m_name; }
};
class AstCoverCrossSelect final : public AstNode {
// Intersection or union of two cross-bin selections
// @astgen op1 := lhsp : Optional[AstNode] // Null for an unsupported selection
// @astgen op2 := rhsp : Optional[AstNode] // Null for an unsupported selection
const bool m_isOr; // Union (||), rather than intersection (&&)
public:
AstCoverCrossSelect(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool isOr)
: ASTGEN_SUPER_CoverCrossSelect(fl)
, m_isOr{isOr} {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstCoverCrossSelect;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isOr() const { return m_isOr; }
string verilogKwd() const override { return isOr() ? "||" : "&&"; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
return m_isOr == VN_DBG_AS(samep, CoverCrossSelect)->m_isOr;
} // LCOV_EXCL_STOP
};
class AstCoverOption final : public AstNode {
// Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr
+39 -4
View File
@@ -1209,8 +1209,44 @@ void AstCoverBin::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "isWildcard", isWildcard());
str << ", \"binsType\": \"" << binsType().ascii() << "\"";
}
void AstCoverBinsof::dump(std::ostream& str) const {
Super::dump(str);
if (isNegated()) str << " [NEGATED]";
}
void AstCoverBinsof::dumpJson(std::ostream& str) const {
Super::dumpJson(str);
dumpJsonBoolIf(str, "isNegated", isNegated());
}
void AstCoverCross::dump(std::ostream& str) const { Super::dump(str); }
void AstCoverCross::dumpJson(std::ostream& str) const { Super::dumpJson(str); }
string AstCoverCrossDType::cppTemplateArgs() const {
return cvtToStr(dimensions()) + ", " + cvtToStr(tuples()) + ", " + cvtToStr(bins()) + ", "
+ cvtToStr(autoBins()) + ", " + cvtToStr(binWords());
}
void AstCoverCrossDType::dump(std::ostream& str) const {
Super::dump(str);
str << " [" << cppTemplateArgs() << "]";
}
void AstCoverCrossDType::dumpJson(std::ostream& str) const {
dumpJsonNumFunc(str, dimensions);
dumpJsonNumFunc(str, tuples);
dumpJsonNumFunc(str, bins);
dumpJsonNumFunc(str, autoBins);
dumpJsonNumFunc(str, binWords);
dumpJsonGen(str);
}
void AstCoverCrossDType::dumpSmall(std::ostream& str) const {
Super::dumpSmall(str);
str << "covercross[" << cppTemplateArgs() << "]";
}
void AstCoverCrossSelect::dump(std::ostream& str) const {
Super::dump(str);
str << (isOr() ? " [OR]" : " [AND]");
}
void AstCoverCrossSelect::dumpJson(std::ostream& str) const {
Super::dumpJson(str);
dumpJsonBoolIf(str, "isOr", isOr());
}
void AstCoverInc::dump(std::ostream& str) const {
Super::dump(str);
str << " -> ";
@@ -2148,6 +2184,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
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() + ">*";
} 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()) + ">*";
@@ -2228,10 +2267,6 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlStdRandomizer";
} else if (bdtypep->isCovergroupInstHandle()) {
info.m_type = "VlCovInstHandle";
} else if (bdtypep->isCovergroupCross()) {
// Borrowed pointer: VlCovergroupInst owns the cross runtime, so its bins outlive the
// SV covergroup object (the coverage DB holds raw count pointers read at write() time)
info.m_type = "VlCoverCross*";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
+12 -11
View File
@@ -93,6 +93,7 @@ class CleanVisitor final : public VNVisitor {
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverCrossDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverpointDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else {
@@ -141,12 +142,12 @@ class CleanVisitor final : public VNVisitor {
computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep);
}
void ensureCleanAndNext(AstNodeExpr* nodep) {
void ensureCleanAndNext(AstNode* nodep) {
// Editing list, careful looping!
for (AstNodeExpr* exprp = nodep; exprp;) {
AstNodeExpr* const nextp = VN_AS(exprp->nextp(), NodeExpr);
ensureClean(exprp);
exprp = nextp;
for (AstNode* argp = nodep; argp;) {
AstNode* const nextp = argp->nextp();
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
argp = nextp;
}
}
@@ -250,9 +251,7 @@ class CleanVisitor final : public VNVisitor {
setClean(nodep, false);
// We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstTraceDecl* nodep) override {} // Nothing to do here
void visit(AstTraceInc* nodep) override {
@@ -288,11 +287,13 @@ class CleanVisitor final : public VNVisitor {
ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
}
void visit(AstCStmt* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstCStmtUser* nodep) override {
iterateChildren(nodep);
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep);
+621 -52
View File
@@ -30,7 +30,10 @@
#include "V3File.h"
#include "V3MemberMap.h"
#include <array>
#include <bitset>
#include <set>
#include <tuple>
#include <unordered_map>
#include <vector>
@@ -75,6 +78,7 @@ class CovergroupExprValidVisitor final : public VNVisitor {
iterateAndNextNull(nodep->selectp());
scanSampleExpression(nodep->iffp());
}
void visit(AstCoverBinsof* nodep) override { scanCoverageExpression(nodep->rangesp()); }
void visit(AstCoverBin* nodep) override {
scanCoverageExpression(nodep->rangesp());
scanSampleExpression(nodep->iffp());
@@ -162,8 +166,15 @@ class FunctionalCoverageVisitor final : public VNVisitor {
std::vector<AstVar*> m_cpVars; // VlCoverpoint member, one per coverpoint
std::vector<AstVar*> m_crossVars; // VlCoverCross member, one per cross
std::map<std::string, AstVar*> m_cpVarMap; // Coverpoint name -> its VlCoverpoint member
struct CrossBinValues final {
AstCoverBin* binp; // Declaration owning this Normal bin
AstNodeExpr* valuep; // Individual array-bin value, or nullptr for a scalar bin
};
struct CoverpointBins final {
uint32_t total = 0; // Number of Normal bins
AstNodeExpr* exprp = nullptr; // Sampled expression, for the value domain
std::vector<CrossBinValues> values; // Values in runtime Normal-bin index order
std::vector<AstCoverBin*> excluded; // State ignore/illegal bins removing values
std::unordered_map<std::string, std::pair<uint32_t, uint32_t>>
spans; // Declared bin name -> first Normal index and number of bins
};
@@ -171,6 +182,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
std::set<AstCoverCross*>
m_droppedCrosses; // Crosses with a bare-variable item: drop (COVERIGN)
std::map<uint32_t, AstCoverpointDType*> m_cpDTypes; // Hit-list bound -> interned dtype
using CrossShape = std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, uint64_t>;
std::map<CrossShape, AstCoverCrossDType*> m_cxDTypes;
AstVar* m_cgInstVarp = nullptr; // __Vcg_inst handle member of the current covergroup
VMemberMap m_memberMap; // Member names cached for fast lookup
@@ -745,6 +758,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
for (AstNode* rp = cbinp->rangesp(); rp; rp = rp->nextp()) {
RangeBounds rb;
if (!constRangeBounds(rp, rb)) return false;
if ((rb.loConstp() && rb.loConstp()->width() > 64)
|| (rb.hiConstp() && rb.hiConstp()->width() > 64)) {
return false; // Use the safe slot-count bound for wide values.
}
const uint64_t lo = rb.loUnbounded() ? 0 : rb.loConstp()->toUQuad();
const uint64_t hi = rb.hiUnbounded() ? maxVal : rb.hiConstp()->toUQuad();
if (lo > hi) return false;
@@ -806,7 +823,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
// One entry per Normal bin (cross slot): its covered intervals.
std::vector<std::vector<std::pair<uint64_t, uint64_t>>> bins;
int slotCount = 0; // == runtime m_normal; the safe fallback bound
bool exact = true;
// Unsigned intervals cannot establish overlap between differently sized signed values.
bool exact = !exprp->isSigned();
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
AstCoverBin* const cbinp = VN_AS(binp, CoverBin);
if (!cbinp->binsType().binIsNormal())
@@ -862,7 +880,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
// A literal C++ argument with no AST equivalent: a 'const char*' string literal (an SV
// string AstConst emits '"..."s', a std::string temporary the runtime cannot borrow), a
// VlCovBinKind enum token, or a '__V' temporary declared by the enclosing AstCStmt.
// VlCovBinKind enum token, a constant selection-word initializer list, or a '__V' temporary
// declared by the enclosing AstCStmt.
static AstCExpr* ctext(FileLine* fl, const std::string& text) {
return new AstCExpr{fl, text};
}
@@ -934,13 +953,22 @@ class FunctionalCoverageVisitor final : public VNVisitor {
}
// Emit a 'this->m_cp->addSingleNamer/addArrayNamer(...)' statement for one bin
AstNodeStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int count) {
AstNodeStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int count,
const std::vector<AstNodeExpr*>& values = {}) {
FileLine* const fl = binp->fileline();
CoverpointBins& bins = m_cpBins.at(cpVarp);
const uint32_t normalCount
= binp->binsType().binIsNormal() ? static_cast<uint32_t>(count < 0 ? 1 : count) : 0;
bins.spans.emplace(binp->name(), std::make_pair(bins.total, normalCount));
bins.total += normalCount;
for (uint32_t i = 0; i < normalCount; ++i) {
bins.values.push_back({binp, values.empty() ? nullptr : values[i]});
}
if (!binp->transp()
&& (binp->binsType() == VCoverBinsType::BINS_IGNORE
|| binp->binsType() == VCoverBinsType::BINS_ILLEGAL)) {
bins.excluded.push_back(binp);
}
// Under --protect-ids the filename and bin name flow into the coverage database
// verbatim, so obfuscate them exactly as line/toggle coverage points are (whole-
// unit filename, per-word bin name). A no-op when --protect-ids is off.
@@ -1030,6 +1058,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
m_cpVars.push_back(cpVarp);
m_cpVarMap[coverpointp->name()] = cpVarp;
m_cpBins.emplace(cpVarp, CoverpointBins{});
m_cpBins.at(cpVarp).exprp = exprp;
// Create the runtime in the instance node first; everything below configures it.
m_constructorp->addStmtsp(makeItemCreate(fl, cpVarp, VCMethod::COVERGROUP_ADD_COVERPOINT));
@@ -1071,7 +1100,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
bool unsupported = false;
std::vector<AstNodeExpr*> values = extractArrayValues(cbinp, exprp, unsupported);
if (unsupported) continue; // bin ignored (COVERIGN emitted); reserve no slot
namerStmts.push_back(makeNamer(cpVarp, cbinp, static_cast<int>(values.size())));
namerStmts.push_back(
makeNamer(cpVarp, cbinp, static_cast<int>(values.size()), values));
for (AstNodeExpr* valuep : values) {
// TODO: A 4-state bin value (e.g. bins b[] = {2'b0x}) must match with ===
// (AstEqCase) per IEEE 1800-2023 19.5.4. == is equivalent under 2-state sim
@@ -1426,30 +1456,542 @@ class FunctionalCoverageVisitor final : public VNVisitor {
return cs;
}
// Append a "{ const bool __Vcx_iffs[] = {<iff>, ...}; <call> }" statement, one entry per
// explicit cross bin in declaration order (true where the bin has no iff). As above, the
// temporary array is literal text because a CMethodHard is one call, not a block.
// Assign the per-bin flags individually: one-bit SV results have integer C++ storage types,
// which may narrow in a bool initializer list but convert implicitly in assignments.
AstCStmt* makeCrossIffsCall(FileLine* fl, const std::vector<AstCoverCrossBin*>& bins,
AstCMethodHard* callp) {
AstCStmt* const cs = new AstCStmt{fl};
cs->add("{ const bool __Vcx_iffs[] = {");
bool first = true;
for (const AstCoverCrossBin* const binp : bins) {
if (!first) cs->add(", ");
first = false;
cs->add("{ bool __Vcx_iffs[" + cvtToStr(bins.size()) + "]; ");
for (size_t i = 0; i < bins.size(); ++i) {
const AstCoverCrossBin* const binp = bins[i];
cs->add("__Vcx_iffs[" + cvtToStr(i) + "] = ");
cs->add(binp->iffp() ? binp->iffp()->cloneTree(false)
: new AstConst{fl, AstConst::BitTrue{}});
cs->add("; ");
}
cs->add("}; ");
cs->add(callp);
cs->add("; }");
return cs;
}
std::vector<AstCoverCrossBin*>
generateCrossBins(AstCoverCross* crossp, AstVar* cxVarp, const std::vector<AstVar*>& cpVars,
const std::map<std::string, uint32_t>& dimensions) {
std::vector<AstCoverCrossBin*> bins;
using CrossSelection = std::vector<uint64_t>;
struct ResolvedCrossBin final {
AstCoverCrossBin* binp;
CrossSelection selection;
};
struct CrossLayout final {
uint32_t tuples = 0;
uint32_t autoBins = 0;
uint64_t binWords = 0;
bool valid = true;
std::vector<ResolvedCrossBin> bins;
};
struct CrossSelectionContext final {
AstCoverCross* crossp; // Cross whose tuple space is being selected
const std::vector<AstVar*>& cpVars; // Feeding coverpoints in dimension order
const std::map<std::string, uint32_t>& dimensions; // Coverpoint name -> dimension
uint32_t tuples; // Size of the Cartesian product
std::vector<uint32_t> strides; // Flat-index stride per dimension
bool valid = true; // False if this explicit bin cannot be implemented
};
struct CrossValueRange final {
V3Number lo; // Inclusive lower bound, sign-extended to the comparison width
V3Number hi; // Inclusive upper bound
V3Number pattern; // Allowed bit values; all X for an ordinary interval
bool singleton = false; // A single value, possibly a wildcard pattern
bool wildcard = false; // A wildcard singleton rather than an exact four-state value
CrossValueRange(AstNode* nodep, int width)
: lo{nodep, width}
, hi{nodep, width}
, pattern{nodep, width} {
pattern.setAllBitsX();
}
};
static std::vector<AstNode*> crossBinValues(const CrossBinValues& bin) {
if (bin.valuep) return {bin.valuep};
std::vector<AstNode*> values;
if (bin.binp->transp()) {
// IEEE 1800-2023 19.6.1: binsof uses the last value of each transition.
for (AstNode* setp = bin.binp->transp(); setp; setp = setp->nextp()) {
AstCoverTransItem* lastp = VN_AS(setp, CoverTransSet)->itemsp();
while (lastp->nextp()) lastp = VN_AS(lastp->nextp(), CoverTransItem);
for (AstNode* valuep = lastp->valuesp(); valuep; valuep = valuep->nextp()) {
values.push_back(valuep);
}
}
} else {
for (AstNode* valuep = bin.binp->rangesp(); valuep; valuep = valuep->nextp()) {
values.push_back(valuep);
}
}
return values;
}
static int crossRangeWidth(AstNode* nodep) {
if (const AstInsideRange* const rangep = VN_CAST(nodep, InsideRange)) {
return std::max(rangep->lhsp()->width(), rangep->rhsp()->width());
}
return nodep->width();
}
static bool crossValueLess(const V3Number& lhs, const V3Number& rhs) {
V3Number result{&lhs};
return !result.opLtS(lhs, rhs).isEqZero();
}
static bool crossRangeBound(AstNode* nodep, AstNodeExpr* exprp, bool upper, bool binValue,
V3Number& result) {
if (VN_IS(nodep, Unbounded)) {
V3Number limit{nodep, exprp->width()};
if (upper) limit.setAllBits1();
if (exprp->isSigned()) {
limit.setBit(exprp->width() - 1, !upper);
result.opExtendS(limit, limit.width());
} else {
result.opAssign(limit);
}
return true;
}
const AstConst* const constp = VN_CAST(nodep, Const);
if (!constp || constp->num().isOpaque()) return false;
if (binValue && exprp->isSigned() && constp->width() <= exprp->width()) {
// Bin bit patterns use the coverpoint's effective type (IEEE 1800-2023 19.5.7).
// Wider values and intersect filters retain their values for domain clipping.
V3Number value{nodep, exprp->width()};
if (constp->isSigned()) {
value.opExtendS(constp->num(), constp->width());
} else {
value.opAssign(constp->num());
}
result.opExtendS(value, value.width());
} else if (constp->isSigned()) {
result.opExtendS(constp->num(), constp->width());
} else {
result.opAssign(constp->num());
}
return true;
}
static CrossValueRange crossValueDomain(AstNode* nodep, int valueWidth, bool isSigned,
int width) {
CrossValueRange domain{nodep, width};
V3Number lo{nodep, valueWidth};
V3Number hi{nodep, valueWidth};
hi.setAllBits1();
if (isSigned) {
lo.setBit(valueWidth - 1, 1);
hi.setBit(valueWidth - 1, 0);
domain.lo.opExtendS(lo, valueWidth);
domain.hi.opExtendS(hi, valueWidth);
} else {
domain.lo.opAssign(lo);
domain.hi.opAssign(hi);
}
return domain;
}
static void intersectCrossRange(CrossValueRange& range, const CrossValueRange& other) {
if (crossValueLess(range.lo, other.lo)) range.lo = other.lo;
if (crossValueLess(other.hi, range.hi)) range.hi = other.hi;
}
static bool crossValueRange(AstNode* nodep, AstNodeExpr* exprp, bool binValue, bool wildcard,
const CrossValueRange& domain, CrossValueRange& range) {
if (const AstInsideRange* const rangep = VN_CAST(nodep, InsideRange)) {
if (!crossRangeBound(rangep->lhsp(), exprp, false, binValue, range.lo)
|| !crossRangeBound(rangep->rhsp(), exprp, true, binValue, range.hi)
|| range.lo.isFourState() || range.hi.isFourState()) {
return false;
}
} else {
range.singleton = true;
if (!crossRangeBound(nodep, exprp, false, binValue, range.lo)) return false;
range.hi = range.lo;
range.wildcard = wildcard;
if (wildcard) {
range.pattern = range.lo;
range.lo = domain.lo;
range.hi = domain.hi;
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
if (constp->isSigned()) {
// Replicated X sign bits are correlated, not independent wildcards.
// The source domain preserves expansion-before-casting (19.5.7).
intersectCrossRange(range, crossValueDomain(nodep, constp->width(), true,
domain.lo.width()));
}
}
}
}
if (!range.lo.isFourState()) intersectCrossRange(range, domain);
return true;
}
enum class CrossMatchResult : uint8_t { MATCH, NO_MATCH, WORK_LIMIT };
enum CrossRangeState : uint8_t {
CROSS_INSIDE_BOUNDS = 0, // Prefix is strictly inside the interval
CROSS_AT_LOWER = 1,
CROSS_AT_UPPER = 2,
CROSS_AT_BOUNDS = CROSS_AT_LOWER | CROSS_AT_UPPER,
CROSS_NO_MATCH = 4 // Prefix cannot match the interval/pattern
};
static constexpr size_t CROSS_MATCH_LINEAR_ALLOWANCE = 4; // Minimum linear traversals
static constexpr size_t CROSS_MATCH_WORK_LIMIT = 1U << 20; // Base bit-step budget per search
static CrossRangeState crossRangeStep(const CrossValueRange& range, CrossRangeState state,
int bit, int value) {
if (state == CROSS_NO_MATCH) return CROSS_NO_MATCH;
// Flipping the sign bit makes signed order lexicographic.
const bool sign = bit == range.pattern.width() - 1;
if (!range.pattern.bitIsXZ(bit) && value != (range.pattern.bitIs1(bit) ^ sign)) {
return CROSS_NO_MATCH;
}
const int low = range.lo.bitIs1(bit) ^ sign;
const int high = range.hi.bitIs1(bit) ^ sign;
if (((state & CROSS_AT_LOWER) && value < low)
|| ((state & CROSS_AT_UPPER) && value > high)) {
return CROSS_NO_MATCH;
}
return static_cast<CrossRangeState>(
((state & CROSS_AT_LOWER) && value == low ? CROSS_AT_LOWER : CROSS_INSIDE_BOUNDS)
| ((state & CROSS_AT_UPPER) && value == high ? CROSS_AT_UPPER : CROSS_INSIDE_BOUNDS));
}
static bool crossWildcardIntersects(const CrossValueRange& range) {
unsigned states = 1U << CROSS_AT_BOUNDS;
for (int bit = range.pattern.width() - 1; bit >= 0 && states; --bit) {
unsigned next = 0;
for (const CrossRangeState state :
{CROSS_INSIDE_BOUNDS, CROSS_AT_LOWER, CROSS_AT_UPPER, CROSS_AT_BOUNDS}) {
if (!(states & (1U << state))) continue;
for (int value = 0; value < 2; ++value) {
const CrossRangeState equal = crossRangeStep(range, state, bit, value);
if (equal != CROSS_NO_MATCH) next |= 1U << equal;
}
}
states = next;
}
return states != 0;
}
static std::array<int, CROSS_NO_MATCH> crossFreeBelow(const CrossValueRange& range) {
const int width = range.pattern.width();
int fixed = width;
int lower = width;
int upper = width;
for (int bit = 0; bit < width; ++bit) {
if (fixed == width && !range.pattern.bitIsXZ(bit)) fixed = bit;
if (lower == width && range.lo.bitIs1(bit)) lower = bit;
if (upper == width && !range.hi.bitIs1(bit)) upper = bit;
}
return {fixed, std::min(fixed, lower), std::min(fixed, upper),
std::min({fixed, lower, upper})};
}
static bool crossRangeContains(const CrossValueRange& range, const V3Number& value) {
if (range.lo.isFourState() || crossValueLess(value, range.lo)
|| crossValueLess(range.hi, value)) {
return false;
}
V3Number result{&value};
return !result.opWildEq(value, range.pattern).isEqZero();
}
static CrossMatchResult crossOutsideExcluded(const CrossValueRange& range,
const std::vector<CrossValueRange>& excluded) {
// Array-bin elements and singleton filters need no prefix search.
if (range.lo.isCaseEq(range.hi)) {
if (!crossRangeContains(range, range.lo)) return CrossMatchResult::NO_MATCH;
return std::none_of(excluded.begin(), excluded.end(),
[&](const CrossValueRange& exclusion) {
return crossRangeContains(exclusion, range.lo);
})
? CrossMatchResult::MATCH
: CrossMatchResult::NO_MATCH;
}
std::vector<const CrossValueRange*> blockers;
std::vector<std::array<int, CROSS_NO_MATCH>> freeBelow;
for (const CrossValueRange& exclusion : excluded) {
if (exclusion.lo.isFourState() || crossValueLess(exclusion.hi, exclusion.lo)
|| crossValueLess(exclusion.hi, range.lo)
|| crossValueLess(range.hi, exclusion.lo)) {
continue;
}
blockers.push_back(&exclusion);
freeBelow.push_back(crossFreeBelow(exclusion));
}
if (blockers.empty()) {
return !range.wildcard || crossWildcardIntersects(range) ? CrossMatchResult::MATCH
: CrossMatchResult::NO_MATCH;
}
struct Frame final {
int bit; // Next bit to assign
std::vector<CrossRangeState> state; // Bound states for candidate and exclusions
int nextValue = 0; // Next bit value to try
};
std::vector<Frame> stack{
{range.pattern.width() - 1,
std::vector<CrossRangeState>(blockers.size() + 1, CROSS_AT_BOUNDS), 0}};
std::set<std::pair<int, std::vector<CrossRangeState>>> failed;
size_t work = 0;
const size_t stepCost = blockers.size() + 1;
const size_t workLimit
= std::max(CROSS_MATCH_WORK_LIMIT, static_cast<size_t>(range.pattern.width())
* stepCost * CROSS_MATCH_LINEAR_ALLOWANCE);
// Seek one witness, pruning prefixes wholly covered by an exclusion. Memoizing
// failed prefixes avoids repeated work; a budget bounds hard wildcard unions.
while (!stack.empty()) {
Frame& frame = stack.back();
if (frame.nextValue == 2) {
failed.emplace(frame.bit, std::move(frame.state));
stack.pop_back();
continue;
}
if (stepCost > workLimit - work) return CrossMatchResult::WORK_LIMIT;
work += stepCost;
const int value = frame.nextValue++;
const CrossRangeState candidate
= crossRangeStep(range, frame.state[0], frame.bit, value);
if (candidate == CROSS_NO_MATCH) continue;
std::vector<CrossRangeState> successor = frame.state;
successor[0] = candidate;
bool covered = false;
for (size_t i = 0; i < blockers.size(); ++i) {
const CrossRangeState match
= crossRangeStep(*blockers[i], frame.state[i + 1], frame.bit, value);
successor[i + 1] = match;
if (match != CROSS_NO_MATCH && freeBelow[i][match] >= frame.bit) {
covered = true;
break;
}
}
if (covered) continue;
if (frame.bit == 0) return CrossMatchResult::MATCH;
const int bit = frame.bit - 1;
if (failed.find({bit, successor}) == failed.end()) {
stack.push_back({bit, std::move(successor), 0});
}
}
return CrossMatchResult::NO_MATCH;
}
static CrossMatchResult crossRangesIntersect(const CrossValueRange& bin,
const CrossValueRange& filter,
const std::vector<CrossValueRange>& excluded,
bool excludeValues) {
if (filter.lo.isFourState() || bin.lo.isFourState()) {
if (!bin.singleton || !filter.singleton || !bin.lo.isCaseEq(filter.lo)) {
return CrossMatchResult::NO_MATCH;
}
return (!excludeValues
|| std::none_of(excluded.begin(), excluded.end(),
[&](const CrossValueRange& range) {
return !range.wildcard && range.singleton
&& bin.lo.isCaseEq(range.lo);
}))
? CrossMatchResult::MATCH
: CrossMatchResult::NO_MATCH;
}
CrossValueRange match = bin;
intersectCrossRange(match, filter);
if (crossValueLess(match.hi, match.lo)) return CrossMatchResult::NO_MATCH;
if (!excludeValues || excluded.empty()) {
return !bin.wildcard || crossWildcardIntersects(match) ? CrossMatchResult::MATCH
: CrossMatchResult::NO_MATCH;
}
return crossOutsideExcluded(match, excluded);
}
static void unsupportedCrossRange(AstCoverBinsof* selectp, bool& valid) {
selectp->v3warn(COVERIGN, "Unsupported: non-constant or non-integral 'intersect' value, "
"or four-state range bound.");
valid = false;
}
static bool crossValueMatchesFilters(AstCoverBinsof* selectp, AstNode* valuep,
AstNodeExpr* exprp, const AstCoverBin* binp,
const CrossValueRange& domain,
const std::vector<CrossValueRange>& filters,
const std::vector<CrossValueRange>& excluded,
bool& valid) {
CrossValueRange range{valuep, domain.lo.width()};
if (!crossValueRange(valuep, exprp, true, binp->isWildcard(), domain, range)) {
unsupportedCrossRange(selectp, valid);
return false;
}
for (const CrossValueRange& filter : filters) {
// State exclusions do not remove values from transition sequences.
const CrossMatchResult result
= crossRangesIntersect(range, filter, excluded, !binp->transp());
if (result == CrossMatchResult::WORK_LIMIT) {
selectp->v3warn(COVERIGN, "Unsupported: 'intersect' exclusion matching exceeds "
"the selection work limit.");
valid = false;
return false;
}
if (result == CrossMatchResult::MATCH) return true;
}
return false;
}
std::vector<bool> selectCoverpointBins(AstCoverBinsof* selectp, const CoverpointBins& bins,
uint32_t first, uint32_t count, bool& valid) {
std::vector<bool> selected(bins.total, false);
std::vector<std::vector<AstNode*>> values;
int width = bins.exprp->width();
for (AstNode* rangep = selectp->rangesp(); rangep; rangep = rangep->nextp()) {
width = std::max(width, crossRangeWidth(rangep));
}
if (selectp->rangesp()) {
for (AstCoverBin* const binp : bins.excluded) {
for (AstNode* rangep = binp->rangesp(); rangep; rangep = rangep->nextp()) {
width = std::max(width, crossRangeWidth(rangep));
}
}
values.reserve(count);
for (uint32_t i = first; i < first + count; ++i) {
values.push_back(crossBinValues(bins.values[i]));
for (AstNode* const valuep : values.back()) {
width = std::max(width, crossRangeWidth(valuep));
}
}
}
// One extra bit preserves both unsigned maxima and negative signed bounds.
++width;
const CrossValueRange domain
= crossValueDomain(selectp, bins.exprp->width(), bins.exprp->isSigned(), width);
std::vector<CrossValueRange> filters;
for (AstNode* rangep = selectp->rangesp(); rangep; rangep = rangep->nextp()) {
CrossValueRange filter{rangep, width};
if (!crossValueRange(rangep, bins.exprp, false, false, domain, filter)) {
unsupportedCrossRange(selectp, valid);
return {};
}
filters.push_back(std::move(filter));
}
std::vector<CrossValueRange> excluded;
if (selectp->rangesp()) {
for (AstCoverBin* const binp : bins.excluded) {
for (AstNode* rangep = binp->rangesp(); rangep; rangep = rangep->nextp()) {
CrossValueRange range{rangep, width};
if (!crossValueRange(rangep, bins.exprp, true, binp->isWildcard(), domain,
range)) {
unsupportedCrossRange(selectp, valid);
return {};
}
excluded.push_back(std::move(range));
}
}
}
for (uint32_t i = first; i < first + count; ++i) {
if (!selectp->rangesp()) {
selected[i] = true;
continue;
}
for (AstNode* const valuep : values[i - first]) {
selected[i]
= crossValueMatchesFilters(selectp, valuep, bins.exprp, bins.values[i].binp,
domain, filters, excluded, valid);
if (!valid) return {};
if (selected[i]) break;
}
}
if (selectp->isNegated()) {
for (uint32_t i = 0; i < bins.total; ++i) selected[i] = !selected[i];
}
return selected;
}
static void setCrossSelectionRange(CrossSelection& selection, uint64_t first, uint64_t end) {
while (first < end) {
const unsigned bit = first % 64;
const unsigned bits = std::min<uint64_t>(64 - bit, end - first);
selection[first / 64] |= (bits == 64 ? ~uint64_t{0} : (uint64_t{1} << bits) - 1)
<< bit;
first += bits;
}
}
CrossSelection crossSelection(AstNode* nodep, CrossSelectionContext& ctx) {
if (AstCoverCrossSelect* const opp = VN_CAST(nodep, CoverCrossSelect)) {
CrossSelection lhs = crossSelection(opp->lhsp(), ctx);
const CrossSelection rhs = crossSelection(opp->rhsp(), ctx);
if (!ctx.valid) return {};
for (size_t i = 0; i < lhs.size(); ++i) {
lhs[i] = opp->isOr() ? lhs[i] | rhs[i] : lhs[i] & rhs[i];
}
return lhs;
}
AstCoverBinsof* const selectp = VN_AS(nodep, CoverBinsof);
const auto dimIt = ctx.dimensions.find(selectp->pointp()->name());
if (dimIt == ctx.dimensions.end()) {
selectp->v3error("binsof coverpoint "
<< selectp->pointp()->prettyNameQ() << " is not an item of cross "
<< ctx.crossp->prettyNameQ() << " (IEEE 1800-2012 19.6.1).");
ctx.valid = false;
return {};
}
const uint32_t dim = dimIt->second;
const CoverpointBins& bins = m_cpBins.at(ctx.cpVars[dim]);
uint32_t first = 0;
uint32_t count = bins.total;
if (!selectp->name().empty()) {
const auto binIt = bins.spans.find(selectp->name());
if (binIt == bins.spans.end()) {
selectp->v3error("Cannot find bin " << selectp->prettyNameQ() << " in coverpoint "
<< selectp->pointp()->prettyNameQ()
<< " (IEEE 1800-2012 19.6.1).");
ctx.valid = false;
return {};
}
first = binIt->second.first;
count = binIt->second.second;
}
const std::vector<bool> selected
= selectCoverpointBins(selectp, bins, first, count, ctx.valid);
if (!ctx.valid) return {};
CrossSelection result((static_cast<uint64_t>(ctx.tuples) + 63) / 64, 0);
const uint64_t stride = ctx.strides[dim];
const uint64_t period = stride * bins.total;
for (uint64_t base = 0; base < ctx.tuples; base += period) {
for (uint32_t i = 0; i < bins.total;) {
if (!selected[i]) {
++i;
continue;
}
const uint32_t begin = i++;
while (i < bins.total && selected[i]) ++i;
setCrossSelectionRange(result, base + begin * stride, base + i * stride);
}
}
return result;
}
CrossLayout resolveCrossLayout(AstCoverCross* crossp, const std::vector<AstVar*>& cpVars,
const std::map<std::string, uint32_t>& dimensions) {
CrossLayout layout;
CrossSelectionContext ctx{crossp, cpVars, dimensions, 0, {}};
uint64_t tuples = std::any_of(cpVars.begin(), cpVars.end(),
[this](AstVar* varp) { return !m_cpBins.at(varp).total; })
? 0
: 1;
ctx.strides.resize(cpVars.size());
for (size_t d = cpVars.size(); d > 0; --d) {
ctx.strides[d - 1] = tuples;
tuples *= m_cpBins.at(cpVars[d - 1]).total;
if (tuples > UINT32_MAX) {
crossp->v3warn(COVERIGN,
"Unsupported: cross coverage with more than 2^32-1 tuples.");
layout.valid = false;
return layout;
}
}
ctx.tuples = tuples;
layout.tuples = tuples;
CrossSelection excluded;
std::set<std::string> names;
for (AstNode* itemp = crossp->binsp(); itemp; itemp = itemp->nextp()) {
AstCoverCrossBin* const binp = VN_AS(itemp, CoverCrossBin);
@@ -1458,38 +2000,58 @@ class FunctionalCoverageVisitor final : public VNVisitor {
<< " (IEEE 1800-2012 19.6.1).");
continue;
}
const AstCoverBinsof* const selectp = VN_AS(binp->selectp(), CoverBinsof);
const auto dimIt = dimensions.find(selectp->pointp()->name());
if (dimIt == dimensions.end()) {
selectp->v3error("binsof coverpoint "
<< selectp->pointp()->prettyNameQ() << " is not an item of cross "
<< crossp->prettyNameQ() << " (IEEE 1800-2012 19.6.1).");
ctx.valid = true;
CrossSelection selection = crossSelection(binp->selectp(), ctx);
if (!ctx.valid || std::all_of(selection.begin(), selection.end(), [](uint64_t word) {
return word == 0;
})) {
continue;
}
const uint32_t dim = dimIt->second;
const CoverpointBins& cpBins = m_cpBins.at(cpVars[dim]);
uint32_t first = 0;
uint32_t count = cpBins.total;
if (!selectp->name().empty()) {
const auto binIt = cpBins.spans.find(selectp->name());
if (binIt == cpBins.spans.end()) {
selectp->v3error("Cannot find bin " << selectp->prettyNameQ()
<< " in coverpoint "
<< selectp->pointp()->prettyNameQ()
<< " (IEEE 1800-2012 19.6.1).");
continue;
}
first = binIt->second.first;
count = binIt->second.second;
if (excluded.empty()) excluded.resize(selection.size(), 0);
for (size_t i = 0; i < selection.size(); ++i) {
excluded[i] |= selection[i];
if (selection[i]) ++layout.binWords;
}
// IEEE 1800-2012 19.6 excludes default, ignored and illegal coverpoint bins
// from cross products. An empty selection is valid, not an unsupported construct.
if (!count) continue;
layout.bins.push_back({binp, std::move(selection)});
}
if (!layout.bins.empty()) {
layout.autoBins = layout.tuples;
for (const uint64_t word : excluded) {
layout.autoBins -= static_cast<uint32_t>(std::bitset<VL_QUADSIZE>{word}.count());
}
}
return layout;
}
AstCoverCrossDType* crossDType(FileLine* fl, uint32_t dimensions, const CrossLayout& layout) {
const uint32_t bins = static_cast<uint32_t>(layout.bins.size());
const CrossShape shape{dimensions, layout.tuples, bins, layout.autoBins, layout.binWords};
AstCoverCrossDType*& typep = m_cxDTypes[shape];
if (!typep) {
typep = new AstCoverCrossDType{fl, dimensions, layout.tuples,
bins, layout.autoBins, layout.binWords};
v3Global.rootp()->typeTablep()->addTypesp(typep);
}
return typep;
}
std::vector<AstCoverCrossBin*> generateCrossBins(AstCoverCross* crossp, AstVar* cxVarp,
const CrossLayout& layout) {
std::vector<AstCoverCrossBin*> bins;
for (const ResolvedCrossBin& resolved : layout.bins) {
AstCoverCrossBin* const binp = resolved.binp;
const CrossSelection& selection = resolved.selection;
FileLine* const fl = binp->fileline();
const bool prot = v3Global.opt.protectIds();
std::string mask = "{";
for (size_t i = 0; i < selection.size(); ++i) {
if (i) mask += ", ";
mask += std::to_string(selection[i]) + "ULL";
}
mask += "}";
m_constructorp->addStmtsp(
itemCall(fl, cxVarp, VCMethod::COVERGROUP_ADD_BIN,
{cnum(fl, dim), cnum(fl, first), cnum(fl, count),
{ctext(fl, mask),
ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot))),
ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))),
cnum(fl, static_cast<uint32_t>(fl->lineno())),
@@ -1532,9 +2094,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
itemp = nextp;
}
const int dims = static_cast<int>(cpVars.size());
const CrossLayout layout = resolveCrossLayout(crossp, cpVars, dimensions);
if (!layout.valid) return;
AstVar* const cxVarp = new AstVar{fl, VVarType::MEMBER, "__Vcx_" + crossp->name(),
basicDType(fl, VBasicDTypeKwd::COVERGROUP_CROSS)};
crossDType(fl, static_cast<uint32_t>(dims), layout)};
m_covergroupp->addMembersp(cxVarp);
m_crossVars.push_back(cxVarp);
m_constructorp->addStmtsp(makeItemCreate(fl, cxVarp, VCMethod::COVERGROUP_ADD_CROSS));
@@ -1552,8 +2116,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))),
cnum(fl, static_cast<uint32_t>(fl->lineno())),
cnum(fl, static_cast<uint32_t>(fl->firstColumn()))})));
const std::vector<AstCoverCrossBin*> bins
= generateCrossBins(crossp, cxVarp, cpVars, dimensions);
const std::vector<AstCoverCrossBin*> bins = generateCrossBins(crossp, cxVarp, layout);
if (v3Global.opt.coverage()) {
const std::string page
= VIdProtect::protectIf("v_covergroup/" + m_covergroupp->name(), prot);
@@ -1565,15 +2128,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
// sample(): after all coverpoints have sampled (cross loop runs after coverpoint loop).
UASSERT_OBJ(m_sampleFuncp, crossp, "sample() CFunc not set for cross");
// The cross reads its coverpoints from its own m_cps, so sample() needs no cps array;
// The cross remembers its feeding coverpoints, so sample() needs no cps array;
// per-bin iff guards still need a temporary array, hence the block form.
const bool hasIffs
= std::any_of(bins.begin(), bins.end(),
[](const AstCoverCrossBin* binp) { return binp->iffp() != nullptr; });
AstNodeStmt* const samplep
= bins.empty() ? static_cast<AstNodeStmt*>(
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE)->makeStmt())
: static_cast<AstNodeStmt*>(makeCrossIffsCall(
fl, bins,
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE_IFFS,
{ctext(fl, "__Vcx_iffs")})));
= !hasIffs ? static_cast<AstNodeStmt*>(
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE)->makeStmt())
: static_cast<AstNodeStmt*>(makeCrossIffsCall(
fl, bins,
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE_IFFS,
{ctext(fl, "__Vcx_iffs")})));
if (AstNodeExpr* const iffp = crossp->iffp()) {
m_sampleFuncp->addStmtsp(new AstIf{fl, iffp->cloneTree(false), samplep});
} else {
@@ -2355,6 +2921,9 @@ public:
~FunctionalCoverageVisitor() override = default;
};
// C++14 requires definitions for constexpr members passed by reference.
constexpr size_t FunctionalCoverageVisitor::CROSS_MATCH_WORK_LIMIT;
//######################################################################
// Functional coverage class functions
+3 -4
View File
@@ -541,7 +541,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
depth + 1, suffix + ".atDefault()", nullptr);
} else if (VN_IS(dtypep, CDType)) {
return ""; // Constructor does it
} else if (VN_IS(dtypep, CoverpointDType)) {
} else if (VN_IS(dtypep, CoverCrossDType) || VN_IS(dtypep, CoverpointDType)) {
return ""; // Covergroup constructor creates the runtime and assigns the pointer
} else if (const AstClassRefDType* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
return adtypep->rawPointer() ? varNameProtected + suffix + " = nullptr;\n" : "";
@@ -596,9 +596,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
return "";
} else if (basicp && (basicp->isRandomGenerator() || basicp->isStdRandomGenerator())) {
return "";
} else if (basicp && (basicp->isCovergroupInstHandle() || basicp->isCovergroupCross())) {
// The handle's own constructor deals with it; the cross is a borrowed pointer that
// the covergroup constructor assigns once it creates the runtime
} else if (basicp && basicp->isCovergroupInstHandle()) {
// The handle's own constructor deals with it.
return "";
} else if (basicp && (basicp->isEvent())) {
return "VlAssignableEvent{};\n";
+4
View File
@@ -819,6 +819,10 @@ public:
const AstCoverpointDType* const cpdtypep
= VN_AS(nodep->dtypep()->skipRefp(), CoverpointDType);
puts("<" + cvtToStr(cpdtypep->hitBound()) + ">");
} else if (nodep->method() == VCMethod::COVERGROUP_ADD_CROSS) {
const AstCoverCrossDType* const cxdtypep
= VN_AS(nodep->dtypep()->skipRefp(), CoverCrossDType);
puts("<" + cxdtypep->cppTemplateArgs() + ">");
}
puts("(");
bool comma = false;
+40 -2
View File
@@ -354,7 +354,38 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
puts(";\n");
}
void visit(AstCoverpointRef* nodep) override { putfs(nodep, nodep->name()); }
void visit(AstCoverBinsof* nodep) override {
putfs(nodep, nodep->isNegated() ? "!binsof(" : "binsof(");
iterateConst(nodep->pointp());
if (!nodep->name().empty()) puts("." + nodep->name());
puts(")");
if (nodep->rangesp()) {
puts(" intersect {");
iterateAndCommaConstNull(nodep->rangesp());
puts("}");
}
}
void visit(AstCoverCrossBin* nodep) override {
putfs(nodep, "bins " + nodep->name() + " = ");
iterateConstNull(nodep->selectp());
if (nodep->iffp()) {
puts(" iff (");
iterateConst(nodep->iffp());
puts(")");
}
puts(";\n");
}
void visit(AstCoverCrossSelect* nodep) override {
putfs(nodep, "(");
iterateConstNull(nodep->lhsp());
putbs(" " + nodep->verilogKwd() + " ");
iterateConstNull(nodep->rhsp());
puts(")");
}
void visit(AstCoverpointRef* nodep) override {
putfs(nodep, nodep->name());
iterateConstNull(nodep->exprp());
}
void visit(AstCoverCross* nodep) override {
putfs(nodep, nodep->name() + ": cross ");
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
@@ -366,7 +397,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateConst(nodep->iffp());
puts(")");
}
puts(";\n");
if (nodep->binsp()) {
puts(" {\n");
iterateAndNextConstNull(nodep->binsp());
puts("}\n");
} else {
puts(";\n");
}
}
void visit(AstCoverTransSet* nodep) override {
puts("(");
@@ -1207,6 +1244,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
}
void visit(AstConst* nodep) override { putfs(nodep, nodep->num().ascii(m_prefixed, true)); }
void visit(AstUnbounded* nodep) override { emitVerilogFormat(nodep, nodep->emitVerilog()); }
// Just iterate
void visit(AstTopScope* nodep) override { iterateChildrenConst(nodep); }
+10
View File
@@ -1447,6 +1447,16 @@ class LinkParseVisitor final : public VNVisitor {
}
}
void visit(AstCoverCrossSelect* nodep) override {
cleanFileline(nodep);
iterateChildren(nodep);
if (!nodep->lhsp()
|| !nodep->rhsp()) { // Due to earlier Unsupported errors dropping only one operand
// would silently change the selected set.
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
}
void visit(AstCoverCross* nodep) override {
cleanFileline(nodep);
// Move options out of the mixed parse-time body, leaving only cross bins.
+10 -3
View File
@@ -2080,13 +2080,13 @@ class WidthVisitor final : public VNVisitor {
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
userIterateAndNext(nodep->optionsp(), nullptr);
}
void visit(AstCoverBin* nodep) override {
// Bin range/value entries are self-determined constant expressions (IEEE 1800-2023
void widthCovergroupRanges(AstNode* rangesp) {
// Bin range/value entries are self-determined expressions (IEEE 1800-2023
// 19.5). Width each plain single-value entry self-determined so a referenced
// parameter acquires a dtype, then constify so the reference folds to the AstConst
// value that V3Covergroup requires. AstInsideRange entries fold their own bounds in
// visit(AstInsideRange).
for (AstNode *nextp, *itemp = nodep->rangesp(); itemp; itemp = nextp) {
for (AstNode *nextp, *itemp = rangesp; itemp; itemp = nextp) {
nextp = itemp->nextp();
if (VN_IS(itemp, InsideRange)) {
userIterate(itemp, nullptr);
@@ -2095,6 +2095,13 @@ class WidthVisitor final : public VNVisitor {
V3Const::constifyEdit(itemp); // itemp may change
}
}
}
void visit(AstCoverBinsof* nodep) override {
userIterateAndNext(nodep->pointp(), nullptr);
widthCovergroupRanges(nodep->rangesp());
}
void visit(AstCoverBin* nodep) override {
widthCovergroupRanges(nodep->rangesp());
userIterateAndNext(nodep->iffp(), nullptr);
userIterateAndNext(nodep->arraySizep(), nullptr);
userIterateAndNext(nodep->transp(), nullptr);
+8 -7
View File
@@ -7407,9 +7407,9 @@ select_expression<nodep>: // ==IEEE: select_expression
select_expression_r
{ $$ = $1; }
| select_expression yP_ANDAND select_expression
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '&&' in coverage select expression"); DEL($1, $3); }
{ $$ = new AstCoverCrossSelect{$2, $1, $3, false}; }
| select_expression yP_OROR select_expression
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '||' in coverage select expression"); DEL($1, $3); }
{ $$ = new AstCoverCrossSelect{$2, $1, $3, true}; }
;
// This non-terminal exists to disambiguate select_expression and make "with" bind tighter
@@ -7418,11 +7418,12 @@ select_expression_r<nodep>:
yBINSOF '(' bins_expression ')'
{ $$ = new AstCoverBinsof{$1, new AstCoverpointRef{$3->fileline(), $3}}; }
| '!' yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($4); }
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($3, $7); }
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($4, $8); }
{ $$ = new AstCoverBinsof{$1, new AstCoverpointRef{$4->fileline(), $4}, true}; }
// // IEEE: covergroup_range_list has the same syntax as range_list
| yBINSOF '(' bins_expression ')' yINTERSECT '{' range_list '}'
{ $$ = new AstCoverBinsof{$1, new AstCoverpointRef{$3->fileline(), $3}, false, $7}; }
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' range_list '}'
{ $$ = new AstCoverBinsof{$1, new AstCoverpointRef{$4->fileline(), $4}, true, $8}; }
| yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($3); }
| '!' yWITH__PAREN '(' cgexpr ')'
+56 -20
View File
@@ -89,40 +89,76 @@
: ... note: In instance 't'
84 | cp_a: coverpoint cp_expr {bins x = {size_var};}
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:89:41: Non-constant expression in bin range; range bounds must be constants
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:87:23: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
: ... note: In instance 't'
87 | bins filtered = binsof(cp_a) intersect {0};
| ^~~~~~
%Error: t/t_covergroup_autobins_bad.v:91:41: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
89 | cp_a: coverpoint cp_expr {bins x = {[size_var : 1]};}
91 | cp_a: coverpoint cp_expr {bins x = {[size_var : 1]};}
| ^
%Error: t/t_covergroup_autobins_bad.v:94:41: Non-constant expression in bin range; range bounds must be constants
%Error: t/t_covergroup_autobins_bad.v:96:41: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
94 | cp_a: coverpoint cp_expr {bins x = {[0 : size_var]};}
96 | cp_a: coverpoint cp_expr {bins x = {[0 : size_var]};}
| ^
%Error: t/t_covergroup_autobins_bad.v:99:36: Four-state (x/z) value in array bins range bound; range bounds must be two-state constants
: ... note: In instance 't'
99 | cp_a: coverpoint cp_expr {bins x[] = {[4'b000x : 4'hF]};}
| ^
%Error: t/t_covergroup_autobins_bad.v:104:36: Four-state (x/z) value in array bins range bound; range bounds must be two-state constants
%Error: t/t_covergroup_autobins_bad.v:101:36: Four-state (x/z) value in array bins range bound; range bounds must be two-state constants
: ... note: In instance 't'
104 | cp_a: coverpoint cp_expr {bins x[] = {[4'h0 : 4'b000x]};}
101 | cp_a: coverpoint cp_expr {bins x[] = {[4'b000x : 4'hF]};}
| ^
%Error: t/t_covergroup_autobins_bad.v:109:36: Non-constant expression in array bins value list; values must be constants
%Error: t/t_covergroup_autobins_bad.v:106:36: Four-state (x/z) value in array bins range bound; range bounds must be two-state constants
: ... note: In instance 't'
109 | cp_a: coverpoint cp_expr {bins x[] = {size_var};}
106 | cp_a: coverpoint cp_expr {bins x[] = {[4'h0 : 4'b000x]};}
| ^
%Error: t/t_covergroup_autobins_bad.v:124:28: binsof coverpoint 'missing' is not an item of cross 'xc' (IEEE 1800-2012 19.6.1).
%Error: t/t_covergroup_autobins_bad.v:111:36: Non-constant expression in array bins value list; values must be constants
: ... note: In instance 't'
124 | bins missing_point = binsof(missing);
111 | cp_a: coverpoint cp_expr {bins x[] = {size_var};}
| ^
%Error: t/t_covergroup_autobins_bad.v:126:28: binsof coverpoint 'missing' is not an item of cross 'xc' (IEEE 1800-2012 19.6.1).
: ... note: In instance 't'
126 | bins missing_point = binsof(missing);
| ^~~~~~
%Error: t/t_covergroup_autobins_bad.v:125:30: binsof coverpoint 'cp_other' is not an item of cross 'xc' (IEEE 1800-2012 19.6.1).
%Error: t/t_covergroup_autobins_bad.v:127:30: binsof coverpoint 'cp_other' is not an item of cross 'xc' (IEEE 1800-2012 19.6.1).
: ... note: In instance 't'
125 | bins uncrossed_point = binsof(cp_other);
127 | bins uncrossed_point = binsof(cp_other);
| ^~~~~~
%Error: t/t_covergroup_autobins_bad.v:126:26: Cannot find bin 'missing' in coverpoint 'cp_a' (IEEE 1800-2012 19.6.1).
%Error: t/t_covergroup_autobins_bad.v:128:26: Cannot find bin 'missing' in coverpoint 'cp_a' (IEEE 1800-2012 19.6.1).
: ... note: In instance 't'
126 | bins missing_bin = binsof(cp_a.missing);
128 | bins missing_bin = binsof(cp_a.missing);
| ^~~~~~
%Error: t/t_covergroup_autobins_bad.v:128:7: Duplicate cross bin 'duplicate' (IEEE 1800-2012 19.6.1).
%Error: t/t_covergroup_autobins_bad.v:130:7: Duplicate cross bin 'duplicate' (IEEE 1800-2012 19.6.1).
: ... note: In instance 't'
128 | bins duplicate = binsof(cp_b);
130 | bins duplicate = binsof(cp_b);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:131:26: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
: ... note: In instance 't'
131 | bins nonconstant = binsof(cp_a) intersect {size_var} || binsof(cp_b);
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:132:32: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
: ... note: In instance 't'
132 | bins nonconstant_range = binsof(cp_a) intersect {[0:size_var]};
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:143:9: Unsupported: cross coverage with more than 2^32-1 tuples.
: ... note: In instance 't'
143 | xc: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f {
| ^~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:146:16: Unsupported: cross coverage with more than 2^32-1 tuples.
: ... note: In instance 't'
146 | auto_only: cross cp_a, cp_b, cp_c, cp_d, cp_e, cp_f;
| ^~~~~
%Error: t/t_covergroup_autobins_bad.v:152:34: Non-constant expression in bin value list; values must be constants
: ... note: In instance 't'
152 | ignore_bins nonconstant = {size_var};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:152:34: Non-constant expression in bin range; values must be constants
: ... note: In instance 't'
152 | ignore_bins nonconstant = {size_var};
| ^~~~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:156:23: Unsupported: non-constant or non-integral 'intersect' value, or four-state range bound.
: ... note: In instance 't'
156 | bins selected = binsof(cp_a) intersect {0};
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:195:23: Unsupported: 'intersect' exclusion matching exceeds the selection work limit.
: ... note: In instance 't'
195 | bins selected = binsof(cp_a.whole) intersect {[0:30'h3fffffff]} && binsof(cp_b);
| ^~~~~~
%Error: Exiting due to
+100 -1
View File
@@ -83,7 +83,9 @@ module t;
covergroup cgx_nc_value; // non-constant value, non-array bin
cp_a: coverpoint cp_expr {bins x = {size_var};}
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
xc: cross cp_a, cp_c;
xc: cross cp_a, cp_c {
bins filtered = binsof(cp_a) intersect {0};
}
endgroup
covergroup cgx_nc_range_lo; // non-constant low bound, non-array range
cp_a: coverpoint cp_expr {bins x = {[size_var : 1]};}
@@ -126,6 +128,99 @@ module t;
bins missing_bin = binsof(cp_a.missing);
bins duplicate = binsof(cp_a);
bins duplicate = binsof(cp_b);
bins nonconstant = binsof(cp_a) intersect {size_var} || binsof(cp_b);
bins nonconstant_range = binsof(cp_a) intersect {[0:size_var]};
}
endgroup
covergroup cgx_binsof_large;
cp_a: coverpoint cp_wide;
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 {
bins selected = binsof(cp_a);
}
auto_only: 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};
ignore_bins nonconstant = {size_var};
}
cp_b: coverpoint cp_expr {bins normal = {0};}
xc: cross cp_a, cp_b {
bins selected = binsof(cp_a) intersect {0};
}
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]};
// These singleton queries must not consume an aggregate nonlinear-search budget.
wildcard ignore_bins removed = {
16'b??1????11???????, 16'b?0????0??1??????, 16'b????1?1????????0, 16'b1??????1?1??????,
16'b???????????0??10, 16'b???????0?????1?0, 16'b????1????1???0??, 16'b?1??0?????1?????,
16'b??????0????0?0??, 16'b?????1?????00???, 16'b???????0??1?0???, 16'b????????0??0???0,
16'b???????0??1????0, 16'b?0??1??????????0, 16'b0?????????0???1?, 16'b???????1????01??,
16'b??0????????1???0, 16'b?1???????1??0???, 16'b?10??0??????????, 16'b10??????1???????,
16'b1???????1????1??, 16'b1????????????1?0, 16'b??1????????01???, 16'b???1???0???????0,
16'b1???????1?1?????, 16'b?10???0?????????, 16'b?1???1??1???????, 16'b10?0????????????,
16'b?00???0?????????, 16'b????????10???1??, 16'b??????1????10???, 16'b??????01???????0,
16'b???1????1???1???, 16'b???1????1??0????, 16'b???1?0????????1?, 16'b????0?0??0??????,
16'b????1??0?????1??, 16'b??0??1????????1?, 16'b?????11????????1, 16'b????1?01????????,
16'b?????0?00???????, 16'b???1????0????1??, 16'b?0????????0???1?, 16'b??????10??????0?,
16'b1?0??0??????????, 16'b10?????0????????, 16'b10?0????????????, 16'b???10??????????0,
16'b1????0????1?????, 16'b?0?????????1??0?, 16'b???0?????1?????0, 16'b?00???????0?????,
16'b????0????1????0?, 16'b0????1??????1???, 16'b?1??1????0??????, 16'b???????0??????10,
16'b?????0??01??????, 16'b?0???1?0????????, 16'b?1????1?1???????, 16'b?????00??0??????
};
}
cp_b: coverpoint cp_expr;
xc: cross cp_a, cp_b {
bins selected = binsof(cp_a.whole) intersect {[0:16'hffff]};
}
endgroup
@@ -144,6 +239,10 @@ module t;
cgx_arr_ncval cgx_arr_ncval_inst = new;
cgx_arr_open cgx_arr_open_inst = new;
cgx_binsof cgx_binsof_inst = new;
cgx_binsof_large cgx_binsof_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;
endmodule
+1 -1
View File
@@ -21,6 +21,6 @@ test.run(cmd=[
test.coverage_filename
],
verilator_run=True)
test.file_grep(merged, r"cg_binsof\.all_products\.combined.*' 10")
test.file_grep(merged, r"cg_binsof\.all_products\.combined.*' (\d+)", 10)
test.passes()
+458
View File
@@ -0,0 +1,458 @@
cg_excluded.cp_a.ignored [ignore]: 0
cg_excluded.cp_a.illegal [illegal]: 0
cg_excluded.cp_a.normal: 4
cg_excluded.cp_b.auto_0: 2
cg_excluded.cp_b.auto_1: 2
cg_excluded.selected.complement [cross]: 4
cg_excluded.selected.kept [cross]: 4
cg_excluded.selected.partial [cross]: 4
cg_excluded_four_state.cp_a.other [ignore]: 0
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
cg_excluded_many.cp_b.auto_0: 1
cg_excluded_many.cp_b.auto_1: 1
cg_excluded_many.selected.kept [cross]: 2
cg_excluded_wide.cp_a.ignored [ignore]: 0
cg_excluded_wide.cp_a.normal: 2
cg_excluded_wide.cp_b.auto_0: 1
cg_excluded_wide.cp_b.auto_1: 1
cg_excluded_wide.selected.kept [cross]: 2
cg_excluded_wildcard.cp_a.band [illegal]: 0
cg_excluded_wildcard.cp_a.empty_range [ignore]: 0
cg_excluded_wildcard.cp_a.normal: 4
cg_excluded_wildcard.cp_a.odds [ignore]: 0
cg_excluded_wildcard.cp_b.auto_0: 2
cg_excluded_wildcard.cp_b.auto_1: 2
cg_excluded_wildcard.selected.kept [cross]: 4
cg_fast_paths.cp_a.first: 16
cg_fast_paths.cp_a.last: 10
cg_fast_paths.cp_a.second: 16
cg_fast_paths.cp_b.first: 15
cg_fast_paths.cp_b.last: 10
cg_fast_paths.cp_b.second: 15
cg_fast_paths.cp_c.first: 16
cg_fast_paths.cp_c.last: 10
cg_fast_paths.cp_c.second: 16
cg_fast_paths.selected.early_a [cross]: 7
cg_fast_paths.selected.early_b [cross]: 7
cg_fast_paths.selected.first_x_last_x_first [cross]: 1
cg_fast_paths.selected.first_x_last_x_last [cross]: 1
cg_fast_paths.selected.first_x_last_x_second [cross]: 1
cg_fast_paths.selected.first_x_second_x_first [cross]: 7
cg_fast_paths.selected.first_x_second_x_last [cross]: 1
cg_fast_paths.selected.first_x_second_x_second [cross]: 7
cg_fast_paths.selected.last_x_first_x_first [cross]: 1
cg_fast_paths.selected.last_x_first_x_last [cross]: 1
cg_fast_paths.selected.last_x_first_x_second [cross]: 1
cg_fast_paths.selected.last_x_last_x_first [cross]: 1
cg_fast_paths.selected.last_x_last_x_second [cross]: 1
cg_fast_paths.selected.last_x_second_x_first [cross]: 1
cg_fast_paths.selected.last_x_second_x_last [cross]: 1
cg_fast_paths.selected.last_x_second_x_second [cross]: 1
cg_fast_paths.selected.late [cross]: 2
cg_fast_paths.selected.second_x_first_x_first [cross]: 7
cg_fast_paths.selected.second_x_first_x_last [cross]: 1
cg_fast_paths.selected.second_x_first_x_second [cross]: 7
cg_fast_paths.selected.second_x_last_x_first [cross]: 1
cg_fast_paths.selected.second_x_last_x_last [cross]: 1
cg_fast_paths.selected.second_x_last_x_second [cross]: 1
cg_fast_paths.selected.second_x_second_x_first [cross]: 7
cg_fast_paths.selected.second_x_second_x_last [cross]: 1
cg_fast_paths.selected.second_x_second_x_second [cross]: 7
cg_fixed_words.cp_a.b0: 4
cg_fixed_words.cp_a.b1: 4
cg_fixed_words.cp_a.b10: 10
cg_fixed_words.cp_a.b11: 10
cg_fixed_words.cp_a.b12: 13
cg_fixed_words.cp_a.b13: 13
cg_fixed_words.cp_a.b14: 13
cg_fixed_words.cp_a.b15: 16
cg_fixed_words.cp_a.b2: 4
cg_fixed_words.cp_a.b3: 4
cg_fixed_words.cp_a.b4: 7
cg_fixed_words.cp_a.b5: 7
cg_fixed_words.cp_a.b6: 7
cg_fixed_words.cp_a.b7: 7
cg_fixed_words.cp_a.b8: 10
cg_fixed_words.cp_a.b9: 10
cg_fixed_words.cp_b.auto_0: 1
cg_fixed_words.cp_b.auto_1: 1
cg_fixed_words.cp_b.auto_10: 1
cg_fixed_words.cp_b.auto_11: 1
cg_fixed_words.cp_b.auto_12: 1
cg_fixed_words.cp_b.auto_13: 1
cg_fixed_words.cp_b.auto_14: 1
cg_fixed_words.cp_b.auto_15: 1
cg_fixed_words.cp_b.auto_2: 1
cg_fixed_words.cp_b.auto_3: 1
cg_fixed_words.cp_b.auto_4: 1
cg_fixed_words.cp_b.auto_5: 1
cg_fixed_words.cp_b.auto_6: 1
cg_fixed_words.cp_b.auto_7: 1
cg_fixed_words.cp_b.auto_8: 1
cg_fixed_words.cp_b.auto_9: 1
cg_fixed_words.guarded.all_values [cross]: 16
cg_fixed_words.guarded.subset [cross]: 2
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
cg_guards.selected.constant_true [cross]: 8
cg_guards.selected.high_bit [cross]: 4
cg_guards.selected.low_bit [cross]: 4
cg_guards.selected.low_slice [cross]: 6
cg_guards.selected.selected_bit [cross]: 3
cg_guards.selected.signed_vector [cross]: 4
cg_guards.selected.unguarded [cross]: 8
cg_guards.selected.whole_vector [cross]: 7
cg_guards.selected.wide_bit [cross]: 4
cg_guards.selected.wide_vector [cross]: 6
cg_hit_words.cp_a.b0: 7
cg_hit_words.cp_a.b1: 5
cg_hit_words.cp_a.b2: 4
cg_hit_words.cp_a.b3: 4
cg_hit_words.cp_a.b4: 4
cg_hit_words.cp_a.b5: 4
cg_hit_words.cp_a.b6: 4
cg_hit_words.cp_a.b7: 5
cg_hit_words.cp_a.b8: 5
cg_hit_words.cp_b.b0: 9
cg_hit_words.cp_b.b1: 6
cg_hit_words.cp_b.b2: 6
cg_hit_words.cp_b.b3: 6
cg_hit_words.cp_b.b4: 6
cg_hit_words.cp_b.b5: 6
cg_hit_words.cp_b.b6: 6
cg_hit_words.cp_b.b7: 6
cg_hit_words.cp_b.b8: 8
cg_hit_words.selected.b1_x_b0 [cross]: 4
cg_hit_words.selected.b1_x_b1 [cross]: 3
cg_hit_words.selected.b1_x_b2 [cross]: 3
cg_hit_words.selected.b1_x_b3 [cross]: 3
cg_hit_words.selected.b1_x_b4 [cross]: 3
cg_hit_words.selected.b1_x_b5 [cross]: 3
cg_hit_words.selected.b1_x_b6 [cross]: 3
cg_hit_words.selected.b1_x_b7 [cross]: 3
cg_hit_words.selected.b1_x_b8 [cross]: 4
cg_hit_words.selected.b2_x_b0 [cross]: 3
cg_hit_words.selected.b2_x_b1 [cross]: 2
cg_hit_words.selected.b2_x_b2 [cross]: 2
cg_hit_words.selected.b2_x_b3 [cross]: 2
cg_hit_words.selected.b2_x_b4 [cross]: 2
cg_hit_words.selected.b2_x_b5 [cross]: 2
cg_hit_words.selected.b2_x_b6 [cross]: 2
cg_hit_words.selected.b2_x_b7 [cross]: 2
cg_hit_words.selected.b2_x_b8 [cross]: 3
cg_hit_words.selected.b3_x_b0 [cross]: 3
cg_hit_words.selected.b3_x_b1 [cross]: 2
cg_hit_words.selected.b3_x_b2 [cross]: 2
cg_hit_words.selected.b3_x_b3 [cross]: 2
cg_hit_words.selected.b3_x_b4 [cross]: 2
cg_hit_words.selected.b3_x_b5 [cross]: 2
cg_hit_words.selected.b3_x_b6 [cross]: 2
cg_hit_words.selected.b3_x_b7 [cross]: 2
cg_hit_words.selected.b3_x_b8 [cross]: 3
cg_hit_words.selected.b4_x_b0 [cross]: 3
cg_hit_words.selected.b4_x_b1 [cross]: 2
cg_hit_words.selected.b4_x_b2 [cross]: 2
cg_hit_words.selected.b4_x_b3 [cross]: 2
cg_hit_words.selected.b4_x_b4 [cross]: 2
cg_hit_words.selected.b4_x_b5 [cross]: 2
cg_hit_words.selected.b4_x_b6 [cross]: 2
cg_hit_words.selected.b4_x_b7 [cross]: 2
cg_hit_words.selected.b4_x_b8 [cross]: 3
cg_hit_words.selected.b5_x_b0 [cross]: 3
cg_hit_words.selected.b5_x_b1 [cross]: 2
cg_hit_words.selected.b5_x_b2 [cross]: 2
cg_hit_words.selected.b5_x_b3 [cross]: 2
cg_hit_words.selected.b5_x_b4 [cross]: 2
cg_hit_words.selected.b5_x_b5 [cross]: 2
cg_hit_words.selected.b5_x_b6 [cross]: 2
cg_hit_words.selected.b5_x_b7 [cross]: 2
cg_hit_words.selected.b5_x_b8 [cross]: 3
cg_hit_words.selected.b6_x_b0 [cross]: 3
cg_hit_words.selected.b6_x_b1 [cross]: 2
cg_hit_words.selected.b6_x_b2 [cross]: 2
cg_hit_words.selected.b6_x_b3 [cross]: 2
cg_hit_words.selected.b6_x_b4 [cross]: 2
cg_hit_words.selected.b6_x_b5 [cross]: 2
cg_hit_words.selected.b6_x_b6 [cross]: 2
cg_hit_words.selected.b6_x_b7 [cross]: 2
cg_hit_words.selected.b6_x_b8 [cross]: 3
cg_hit_words.selected.boundary [cross]: 2
cg_hit_words.selected.ends [cross]: 4
cg_hit_words.selected.high [cross]: 4
cg_hit_words.selected.low [cross]: 3
cg_hit_words.single_guarded.b1_x_b0 [cross]: 4
cg_hit_words.single_guarded.b1_x_b1 [cross]: 3
cg_hit_words.single_guarded.b1_x_b2 [cross]: 3
cg_hit_words.single_guarded.b1_x_b3 [cross]: 3
cg_hit_words.single_guarded.b1_x_b4 [cross]: 3
cg_hit_words.single_guarded.b1_x_b5 [cross]: 3
cg_hit_words.single_guarded.b1_x_b6 [cross]: 3
cg_hit_words.single_guarded.b1_x_b7 [cross]: 3
cg_hit_words.single_guarded.b1_x_b8 [cross]: 4
cg_hit_words.single_guarded.b2_x_b0 [cross]: 3
cg_hit_words.single_guarded.b2_x_b1 [cross]: 2
cg_hit_words.single_guarded.b2_x_b2 [cross]: 2
cg_hit_words.single_guarded.b2_x_b3 [cross]: 2
cg_hit_words.single_guarded.b2_x_b4 [cross]: 2
cg_hit_words.single_guarded.b2_x_b5 [cross]: 2
cg_hit_words.single_guarded.b2_x_b6 [cross]: 2
cg_hit_words.single_guarded.b2_x_b7 [cross]: 2
cg_hit_words.single_guarded.b2_x_b8 [cross]: 3
cg_hit_words.single_guarded.b3_x_b0 [cross]: 3
cg_hit_words.single_guarded.b3_x_b1 [cross]: 2
cg_hit_words.single_guarded.b3_x_b2 [cross]: 2
cg_hit_words.single_guarded.b3_x_b3 [cross]: 2
cg_hit_words.single_guarded.b3_x_b4 [cross]: 2
cg_hit_words.single_guarded.b3_x_b5 [cross]: 2
cg_hit_words.single_guarded.b3_x_b6 [cross]: 2
cg_hit_words.single_guarded.b3_x_b7 [cross]: 2
cg_hit_words.single_guarded.b3_x_b8 [cross]: 3
cg_hit_words.single_guarded.b4_x_b0 [cross]: 3
cg_hit_words.single_guarded.b4_x_b1 [cross]: 2
cg_hit_words.single_guarded.b4_x_b2 [cross]: 2
cg_hit_words.single_guarded.b4_x_b3 [cross]: 2
cg_hit_words.single_guarded.b4_x_b4 [cross]: 2
cg_hit_words.single_guarded.b4_x_b5 [cross]: 2
cg_hit_words.single_guarded.b4_x_b6 [cross]: 2
cg_hit_words.single_guarded.b4_x_b7 [cross]: 2
cg_hit_words.single_guarded.b4_x_b8 [cross]: 3
cg_hit_words.single_guarded.b5_x_b0 [cross]: 3
cg_hit_words.single_guarded.b5_x_b1 [cross]: 2
cg_hit_words.single_guarded.b5_x_b2 [cross]: 2
cg_hit_words.single_guarded.b5_x_b3 [cross]: 2
cg_hit_words.single_guarded.b5_x_b4 [cross]: 2
cg_hit_words.single_guarded.b5_x_b5 [cross]: 2
cg_hit_words.single_guarded.b5_x_b6 [cross]: 2
cg_hit_words.single_guarded.b5_x_b7 [cross]: 2
cg_hit_words.single_guarded.b5_x_b8 [cross]: 3
cg_hit_words.single_guarded.b6_x_b0 [cross]: 3
cg_hit_words.single_guarded.b6_x_b1 [cross]: 2
cg_hit_words.single_guarded.b6_x_b2 [cross]: 2
cg_hit_words.single_guarded.b6_x_b3 [cross]: 2
cg_hit_words.single_guarded.b6_x_b4 [cross]: 2
cg_hit_words.single_guarded.b6_x_b5 [cross]: 2
cg_hit_words.single_guarded.b6_x_b6 [cross]: 2
cg_hit_words.single_guarded.b6_x_b7 [cross]: 2
cg_hit_words.single_guarded.b6_x_b8 [cross]: 3
cg_hit_words.single_guarded.b7_x_b0 [cross]: 4
cg_hit_words.single_guarded.b7_x_b1 [cross]: 3
cg_hit_words.single_guarded.b7_x_b2 [cross]: 3
cg_hit_words.single_guarded.b7_x_b3 [cross]: 3
cg_hit_words.single_guarded.b7_x_b4 [cross]: 3
cg_hit_words.single_guarded.b7_x_b5 [cross]: 3
cg_hit_words.single_guarded.b7_x_b6 [cross]: 3
cg_hit_words.single_guarded.b7_x_b7 [cross]: 3
cg_hit_words.single_guarded.b7_x_b8 [cross]: 4
cg_hit_words.single_guarded.ends [cross]: 4
cg_hit_words.whole.all_values [cross]: 10
cg_narrow_wildcard.cp_b.auto_0: 2
cg_narrow_wildcard.cp_b.auto_1: 2
cg_narrow_wildcard.cp_s.outside [ignore]: 0
cg_narrow_wildcard.cp_s.w: 4
cg_narrow_wildcard.cp_u.w: 4
cg_narrow_wildcard.signed_values.complement [cross]: 4
cg_narrow_wildcard.signed_values.negative [cross]: 4
cg_narrow_wildcard.signed_values.positive [cross]: 4
cg_narrow_wildcard.unsigned_values.positive [cross]: 4
cg_numeric.cp_a.encoded: 2
cg_numeric.cp_a.encoded_range: 2
cg_numeric.cp_a.narrow_signed: 2
cg_numeric.cp_a.negative: 2
cg_numeric.cp_a.positive: 2
cg_numeric.cp_a.zero: 2
cg_numeric.cp_b.high: 3
cg_numeric.cp_b.low: 3
cg_numeric.numeric.narrow_signed_x_high [cross]: 1
cg_numeric.numeric.narrow_signed_x_low [cross]: 1
cg_numeric.numeric.negative_high [cross]: 1
cg_numeric.numeric.negative_x_low [cross]: 1
cg_numeric.numeric.nonnegative_low [cross]: 2
cg_numeric.numeric.positive_x_high [cross]: 1
cg_numeric.numeric.typed_negative [cross]: 2
cg_numeric.numeric.zero_x_high [cross]: 1
cg_partial.cp_a.auto_0: 2
cg_partial.cp_a.auto_1: 2
cg_partial.cp_b.auto_0: 2
cg_partial.cp_b.auto_1: 2
cg_partial.selected.auto_1_x_auto_1 [cross]: 1
cg_partial.selected.either_zero [cross]: 3
cg_precedence.a.auto_0: 4
cg_precedence.a.auto_1: 5
cg_precedence.b.auto_0: 5
cg_precedence.b.auto_1: 4
cg_precedence.c.auto_0: 5
cg_precedence.c.auto_1: 4
cg_precedence.three_axes.auto_1_x_auto_1_x_auto_0 [cross]: 1
cg_precedence.three_axes.grouped [cross]: 3
cg_precedence.three_axes.mixed [cross]: 4
cg_precedence.three_axes.ungrouped [cross]: 5
cg_registry.cp_a.auto_0: 2
cg_registry.cp_a.auto_1: 1
cg_registry.cp_b.auto_0: 2
cg_registry.cp_b.auto_1: 1
cg_registry.selected.auto_0_x_auto_0 [cross]: 1
cg_registry.selected.auto_1_x_auto_1 [cross]: 0
cg_registry.selected.off_diagonal [cross]: 2
cg_sets.cp_a.arrayed[0]: 2
cg_sets.cp_a.arrayed[1]: 2
cg_sets.cp_a.high: 4
cg_sets.cp_a.ignored [ignore]: 0
cg_sets.cp_a.low: 8
cg_sets.cp_a.overlap: 8
cg_sets.cp_a.rest [default]: 4
cg_sets.cp_b.both: 24
cg_sets.cp_b.one: 12
cg_sets.cp_b.zero: 12
cg_sets.guarded.arrayed[0]_x_both [cross]: 2
cg_sets.guarded.arrayed[0]_x_zero [cross]: 1
cg_sets.guarded.arrayed[1]_x_both [cross]: 2
cg_sets.guarded.arrayed[1]_x_zero [cross]: 1
cg_sets.guarded.high_x_both [cross]: 4
cg_sets.guarded.high_x_one [cross]: 2
cg_sets.guarded.high_x_zero [cross]: 2
cg_sets.guarded.low_x_both [cross]: 6
cg_sets.guarded.low_x_zero [cross]: 3
cg_sets.guarded.overlap_x_both [cross]: 6
cg_sets.guarded.overlap_x_one [cross]: 3
cg_sets.guarded.overlap_x_zero [cross]: 3
cg_sets.guarded.selected [cross]: 4
cg_sets.logic_ops.all_bins [cross]: 20
cg_sets.logic_ops.both [cross]: 4
cg_sets.logic_ops.either [cross]: 14
cg_sets.logic_ops.named_not_hit [cross]: 16
cg_sets.logic_ops.named_not_miss [cross]: 20
cg_sets.logic_ops.negated [cross]: 16
cg_sets.logic_ops.negated_range [cross]: 8
cg_sets.logic_ops.not_hit_negation [cross]: 8
cg_sets.logic_ops.repeated [cross]: 8
cg_sets.ranges.lower [cross]: 8
cg_sets.ranges.middle [cross]: 12
cg_sets.ranges.named [cross]: 2
cg_sets.ranges.upper [cross]: 2
cg_sets.ranges.values [cross]: 14
cg_transition.cp_a.combined: 4
cg_transition.cp_a.first: 2
cg_transition.cp_a.second: 2
cg_transition.cp_a.value: 2
cg_transition.cp_b.auto_0: 5
cg_transition.cp_b.auto_1: 5
cg_transition.transitions.ending_one [cross]: 4
cg_transition.transitions.named [cross]: 4
cg_transition.transitions.not_ending_one [cross]: 4
cg_transition_ignore.cp_a.seq: 2
cg_transition_ignore.cp_a.value_only [ignore]: 2
cg_transition_ignore.cp_b.auto_0: 2
cg_transition_ignore.cp_b.auto_1: 2
cg_transition_ignore.selected.kept [cross]: 2
cg_wildcard.cp_a.high: 2
cg_wildcard.cp_a.odd: 2
cg_wildcard.cp_b.auto_0: 2
cg_wildcard.cp_b.auto_1: 2
cg_wildcard.wildcard_range.interior [cross]: 2
cg_wildcard.wildcard_range.other [cross]: 2
cg_wildcard.wildcard_range.signed_pattern [cross]: 2
cg_words.all_tuples.all_bins [cross]: 81
cg_words.cp_a.values[0]: 9
cg_words.cp_a.values[1]: 9
cg_words.cp_a.values[2]: 9
cg_words.cp_a.values[3]: 9
cg_words.cp_a.values[4]: 9
cg_words.cp_a.values[5]: 9
cg_words.cp_a.values[6]: 9
cg_words.cp_a.values[7]: 9
cg_words.cp_a.values[8]: 9
cg_words.cp_b.values[0]: 9
cg_words.cp_b.values[1]: 9
cg_words.cp_b.values[2]: 9
cg_words.cp_b.values[3]: 9
cg_words.cp_b.values[4]: 9
cg_words.cp_b.values[5]: 9
cg_words.cp_b.values[6]: 9
cg_words.cp_b.values[7]: 9
cg_words.cp_b.values[8]: 9
cg_words.partial.boundary [cross]: 9
cg_words.partial.either [cross]: 17
cg_words.partial.last_row [cross]: 1
cg_words.partial.values[0]_x_values[0] [cross]: 1
cg_words.partial.values[0]_x_values[1] [cross]: 1
cg_words.partial.values[0]_x_values[2] [cross]: 1
cg_words.partial.values[0]_x_values[3] [cross]: 1
cg_words.partial.values[0]_x_values[4] [cross]: 1
cg_words.partial.values[0]_x_values[5] [cross]: 1
cg_words.partial.values[0]_x_values[6] [cross]: 1
cg_words.partial.values[0]_x_values[7] [cross]: 1
cg_words.partial.values[1]_x_values[0] [cross]: 1
cg_words.partial.values[1]_x_values[1] [cross]: 1
cg_words.partial.values[1]_x_values[2] [cross]: 1
cg_words.partial.values[1]_x_values[3] [cross]: 1
cg_words.partial.values[1]_x_values[4] [cross]: 1
cg_words.partial.values[1]_x_values[5] [cross]: 1
cg_words.partial.values[1]_x_values[6] [cross]: 1
cg_words.partial.values[1]_x_values[7] [cross]: 1
cg_words.partial.values[2]_x_values[0] [cross]: 1
cg_words.partial.values[2]_x_values[1] [cross]: 1
cg_words.partial.values[2]_x_values[2] [cross]: 1
cg_words.partial.values[2]_x_values[3] [cross]: 1
cg_words.partial.values[2]_x_values[4] [cross]: 1
cg_words.partial.values[2]_x_values[5] [cross]: 1
cg_words.partial.values[2]_x_values[6] [cross]: 1
cg_words.partial.values[2]_x_values[7] [cross]: 1
cg_words.partial.values[3]_x_values[0] [cross]: 1
cg_words.partial.values[3]_x_values[1] [cross]: 1
cg_words.partial.values[3]_x_values[2] [cross]: 1
cg_words.partial.values[3]_x_values[3] [cross]: 1
cg_words.partial.values[3]_x_values[4] [cross]: 1
cg_words.partial.values[3]_x_values[5] [cross]: 1
cg_words.partial.values[3]_x_values[6] [cross]: 1
cg_words.partial.values[3]_x_values[7] [cross]: 1
cg_words.partial.values[4]_x_values[0] [cross]: 1
cg_words.partial.values[4]_x_values[1] [cross]: 1
cg_words.partial.values[4]_x_values[2] [cross]: 1
cg_words.partial.values[4]_x_values[3] [cross]: 1
cg_words.partial.values[4]_x_values[4] [cross]: 1
cg_words.partial.values[4]_x_values[5] [cross]: 1
cg_words.partial.values[4]_x_values[6] [cross]: 1
cg_words.partial.values[4]_x_values[7] [cross]: 1
cg_words.partial.values[5]_x_values[0] [cross]: 1
cg_words.partial.values[5]_x_values[1] [cross]: 1
cg_words.partial.values[5]_x_values[2] [cross]: 1
cg_words.partial.values[5]_x_values[3] [cross]: 1
cg_words.partial.values[5]_x_values[4] [cross]: 1
cg_words.partial.values[5]_x_values[5] [cross]: 1
cg_words.partial.values[5]_x_values[6] [cross]: 1
cg_words.partial.values[5]_x_values[7] [cross]: 1
cg_words.partial.values[6]_x_values[0] [cross]: 1
cg_words.partial.values[6]_x_values[1] [cross]: 1
cg_words.partial.values[6]_x_values[2] [cross]: 1
cg_words.partial.values[6]_x_values[3] [cross]: 1
cg_words.partial.values[6]_x_values[4] [cross]: 1
cg_words.partial.values[6]_x_values[5] [cross]: 1
cg_words.partial.values[6]_x_values[6] [cross]: 1
cg_words.partial.values[6]_x_values[7] [cross]: 1
cg_words.partial.values[8]_x_values[1] [cross]: 1
cg_words.partial.values[8]_x_values[2] [cross]: 1
cg_words.partial.values[8]_x_values[3] [cross]: 1
cg_words.partial.values[8]_x_values[4] [cross]: 1
cg_words.partial.values[8]_x_values[5] [cross]: 1
cg_words.partial.values[8]_x_values[6] [cross]: 1
cg_words.partial.values[8]_x_values[7] [cross]: 1
cg_zero_product.cp_a.zero: 1
cg_zero_product.cp_empty.other [default]: 1
+29
View File
@@ -0,0 +1,29 @@
#!/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', 'ms')
# The exclusion checks intentionally reference ignore/default bins.
test.ms_run_flags += ['-suppress', '13196']
test.compile(verilator_flags2=[
'--coverage', '--debug-self-test', '--dump-tree', '--dump-tree-json', '--timing'
],
ms_flags2=['-suppress', '13196'],
timing_loop=True,
threads=(2 if test.vltmt else 1))
test.execute()
if test.vlt_all:
coverage_covergroup_common.covergroup_coverage_report(test)
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()
+557
View File
@@ -0,0 +1,557 @@
// 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 checkd(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__, `__LINE__, (gotv), (expv)); `stop; end while (0);
`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;
timeunit 1ns; timeprecision 1ps;
bit clk = 0;
always #5 clk = ~clk;
int cyc = 0;
covergroup cg_sets with function sample (bit [6:0] a, bit b, bit enabled);
cp_a: coverpoint a {
bins low = {[0 : 3]};
bins overlap = {[2 : 5]};
bins high = {[6 : 7]};
bins arrayed[] = {8, 9};
ignore_bins ignored = {12};
bins rest = default;
}
cp_b: coverpoint b {bins zero = {0}; bins one = {1}; bins both = {0, 1};}
ranges: cross cp_a, cp_b{
// Intersect selects entire bins, including values outside the filter.
bins middle = binsof (cp_a) intersect {
[3 : 4]
};
bins named = binsof (cp_a.arrayed) intersect {9};
bins lower = binsof (cp_a) intersect {[$ : 0]};
bins upper = binsof (cp_a) intersect {[9 : $]};
bins values = binsof (cp_a) intersect {1, [7 : 8]};
bins absent = binsof (cp_a) intersect {30};
bins outside_domain = binsof (cp_a) intersect {128};
bins reversed = binsof (cp_a) intersect {[5 : 3]};
// Negative signed filters must not wrap into an unsigned bin's bit pattern.
bins unsigned_negative = binsof (cp_b.one) intersect {
1'sb1
};
}
logic_ops: cross cp_a, cp_b{
bins negated = !binsof (cp_a.low);
bins negated_range = !binsof (cp_a) intersect {[3 : 4]};
bins named_not_hit = !binsof (cp_a.low) intersect {3};
bins named_not_miss = !binsof (cp_a.low) intersect {4};
bins either = binsof (cp_a.low) || binsof (cp_b.one);
bins both = binsof (cp_a.low) && binsof (cp_b.one);
bins repeated = binsof (cp_a.low) || binsof (cp_a.low);
bins all_bins = binsof (cp_a.low) || !binsof (cp_a.low);
bins no_bins = !binsof (cp_a);
// Distinct bins may overlap in values but never in bin identity.
bins no_tuple = binsof (cp_a.low) && binsof (cp_a.overlap);
bins not_hit_negation = binsof (cp_a.low) && !binsof (cp_a.overlap);
bins excluded = binsof (cp_a.ignored) || binsof (cp_a.rest);
}
guarded: cross cp_a, cp_b iff (enabled) {
bins selected = (binsof(cp_a.low) || binsof(cp_a.arrayed))
&& binsof(cp_b.one) iff (a != 0);
}
endgroup
covergroup cg_partial with function sample (bit a, bit b);
cp_a: coverpoint a;
cp_b: coverpoint b;
// Each item has two bins. The union consumes three tuples, leaving one
// explicit bin and the automatic (1,1) bin in the cross.
selected: cross cp_a, cp_b{
bins either_zero = binsof (cp_a) intersect {0} || binsof (cp_b) intersect {0};
}
endgroup
covergroup cg_zero_product with function sample (bit value);
cp_a: coverpoint value {bins zero = {0};}
cp_empty: coverpoint value {bins other = default;}
selected: cross cp_a, cp_empty;
endgroup
covergroup cg_fixed_words with function sample (bit [3:0] a, bit [3:0] b, bit enable);
cp_a: coverpoint a {
bins b0 = {0};
bins b1 = {[0 : 1]};
bins b2 = {[0 : 2]};
bins b3 = {[0 : 3]};
bins b4 = {[0 : 4]};
bins b5 = {[0 : 5]};
bins b6 = {[0 : 6]};
bins b7 = {[0 : 7]};
bins b8 = {[0 : 8]};
bins b9 = {[0 : 9]};
bins b10 = {[0 : 10]};
bins b11 = {[0 : 11]};
bins b12 = {[0 : 12]};
bins b13 = {[0 : 13]};
bins b14 = {[0 : 14]};
bins b15 = {[0 : 15]};
}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{bins all_values = binsof (cp_a);}
sparse: cross cp_a, cp_b{
bins all_values = binsof (cp_a);
bins subset = binsof (cp_a.b0);
}
guarded: cross cp_a, cp_b{
bins all_values = binsof (cp_a);
bins subset = binsof (cp_a.b0) iff (enable);
}
endgroup
covergroup cg_precedence with function sample (bit a, bit b, bit c);
coverpoint a;
coverpoint b;
coverpoint c;
three_axes: cross a, b, c{
bins ungrouped = binsof(a) intersect {0}
|| binsof(b) intersect {0} && binsof(c) intersect {
1
};
bins grouped = (binsof (a) intersect {0} || binsof (b) intersect {
0
}) && binsof (c) intersect {
1
};
bins mixed = !binsof (a) intersect {0} && (binsof (b) intersect {0} || !binsof (c) intersect {
0
});
}
endgroup
typedef bit signed [6:0] signed_t;
typedef bit [64:0] wide_t;
localparam wide_t LOW = 65'h0_ffff_ffff_ffff_ffff;
localparam wide_t HIGH = 65'h1_0000_0000_0000_0000;
covergroup cg_numeric with function sample (signed_t a, wide_t b);
cp_a: coverpoint a {
bins negative = {[-4 : -1]};
bins encoded = {7'b1111111};
bins encoded_range = {[7'd120 : 7'd127]};
bins narrow_signed = {-7'sd1};
bins zero = {0};
bins positive = {[1 : 4]};
}
cp_b: coverpoint b {bins low = {LOW}; bins high = {HIGH};}
numeric: cross cp_a, cp_b{
bins negative_high = binsof (cp_a) intersect {[-3 : -2]} && binsof (cp_b) intersect {
[HIGH : $]
};
bins nonnegative_low = !binsof (cp_a) intersect {[$ : -1]} && binsof (cp_b) intersect {LOW};
bins typed_negative = binsof(cp_a.encoded) intersect {-1}
|| binsof(cp_a.encoded_range) intersect {
-2
};
bins unsigned_negative = binsof (cp_b.low) intersect {64'shffff_ffff_ffff_ffff};
}
endgroup
covergroup cg_transition with function sample (bit [6:0] a, bit b);
cp_a: coverpoint a {
bins first = (0 => 1);
bins second = (2 => 3);
bins combined = (0 => 1), (2 => 3);
bins value = {4};
}
cp_b: coverpoint b;
transitions: cross cp_a, cp_b{
bins ending_one = binsof (cp_a) intersect {1};
bins not_ending_one = !binsof (cp_a) intersect {1};
bins named = binsof (cp_a.combined) intersect {3};
bins not_initial = binsof (cp_a.first) intersect {0};
}
endgroup
covergroup cg_wildcard with function sample (signed_t a, bit b);
cp_a: coverpoint a {wildcard bins odd = {7'b??????1}; bins high = {[32 : 35]};}
cp_b: coverpoint b;
wildcard_range: cross cp_a, cp_b{
// Neither endpoint matches, but the interior value 3 does.
bins interior = binsof (cp_a) intersect {
[2 : 4]
};
bins even = binsof (cp_a) intersect {2};
bins other = !binsof (cp_a) intersect {[2 : 4]};
bins signed_pattern = binsof (cp_a.odd) intersect {[-4 : -1]};
}
endgroup
covergroup cg_words with function sample (bit [6:0] a, bit [6:0] b);
cp_a: coverpoint a {bins values[] = {[0 : 8]};}
cp_b: coverpoint b {bins values[] = {[0 : 8]};}
partial: cross cp_a, cp_b{
bins boundary = binsof (cp_a) intersect {7};
bins either = binsof (cp_a) intersect {7} || binsof (cp_b) intersect {8};
bins last_row = binsof (cp_a) intersect {8} && binsof (cp_b) intersect {0};
}
all_tuples: cross cp_a, cp_b{bins all_bins = binsof (cp_a);}
endgroup
covergroup cg_fast_paths with function sample (
bit [2:0] a, bit [2:0] b, bit [2:0] c, bit enable_a, bit enable_b, bit enable_late
);
cp_a: coverpoint a {bins first = {0, 1}; bins second = {0, 2}; bins last = {3};}
cp_b: coverpoint b {bins first = {0, 1}; bins second = {0, 2}; bins last = {3};}
cp_c: coverpoint c {bins first = {0, 1}; bins second = {0, 2}; bins last = {3};}
selected: cross cp_a, cp_b, cp_c{
bins early_a = binsof (cp_a.first) && binsof (cp_b.first) iff (enable_a);
bins early_b = binsof (cp_a.first) && binsof (cp_b.first) iff (enable_b);
bins late = binsof (cp_a.last) && binsof (cp_b.last) && binsof (cp_c.last) iff (enable_late);
}
endgroup
covergroup cg_guards with function sample (
bit [6:0] enables, bit [64:0] wide_enable, bit signed [6:0] signed_enable, bit [2:0] index
);
cp_a: coverpoint 1'b0 {bins zero = {0};}
cp_b: coverpoint 1'b0 {bins zero = {0};}
selected: cross cp_a, cp_b{
bins low_bit = binsof (cp_a) iff (enables[0]);
bins high_bit = binsof (cp_a) iff (enables[1]);
bins selected_bit = binsof (cp_a) iff (enables[index]);
bins low_slice = binsof (cp_a) iff (|enables[1:0]);
bins whole_vector = binsof (cp_a) iff (|enables);
bins wide_bit = binsof (cp_a) iff (wide_enable[0]);
bins wide_vector = binsof (cp_a) iff (|wide_enable);
bins signed_vector = binsof (cp_a) iff (|signed_enable);
bins constant_true = binsof (cp_a) iff (1'b1);
bins unguarded = binsof (cp_a);
}
endgroup
covergroup cg_hit_words with function sample (bit [3:0] a, bit [3:0] b, bit [2:0] enables);
cp_a: coverpoint a {
bins b0 = {0, 1};
bins b1 = {0, 2};
bins b2 = {0, 3};
bins b3 = {0, 4};
bins b4 = {0, 5};
bins b5 = {0, 6};
bins b6 = {0, 7};
bins b7 = {0, 8};
bins b8 = {0, 9};
}
cp_b: coverpoint b {
bins b0 = {0, 1};
bins b1 = {0, 2};
bins b2 = {0, 3};
bins b3 = {0, 4};
bins b4 = {0, 5};
bins b5 = {0, 6};
bins b6 = {0, 7};
bins b7 = {0, 8};
bins b8 = {0, 9};
}
selected: cross cp_a, cp_b{
bins low = binsof (cp_a.b0) && binsof (cp_b.b0) iff (enables[0]);
bins boundary = binsof (cp_a.b7) iff (enables[1]);
bins high = binsof (cp_a.b8) && binsof (cp_b.b8);
bins ends = binsof (cp_a.b0) || binsof (cp_a.b8) iff (enables[2]);
}
whole: cross cp_a, cp_b{bins all_values = binsof (cp_a);}
// A multiword selection remains excluded from automatic bins when its guard is false.
single_guarded: cross cp_a, cp_b{
bins ends = binsof (cp_a.b0) || binsof (cp_a.b8) iff (enables[2]);
}
endgroup
// Check four-state bin identities without relying on four-state sampling.
covergroup cg_four_state with function sample (logic [2:0] a, bit b);
cp_a: coverpoint a {bins known = {3'b001}; bins xstate = {3'bx01}; bins zstate = {3'bz01};}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins exact_x = binsof (cp_a) intersect {3'bx01};
bins exact_z = binsof (cp_a) intersect {3'bz01};
bins not_x = !binsof (cp_a) intersect {3'bx01};
}
endgroup
covergroup cg_narrow_wildcard with function sample (signed_t a, bit b);
cp_s: coverpoint a {wildcard bins w = {3'sb?01}; ignore_bins outside = {7'sd7};}
cp_u: coverpoint 7'd1 {wildcard bins w = {3'sb?01};}
cp_b: coverpoint b;
signed_values: cross cp_s, cp_b{
bins positive = binsof (cp_s.w) intersect {1};
bins negative = binsof (cp_s.w) intersect {-3};
bins not_expanded = binsof (cp_s.w) intersect {5};
bins pattern_miss = binsof (cp_s.w) intersect {0};
bins complement = !binsof (cp_s.w) intersect {5};
}
unsigned_values: cross cp_u, cp_b{
bins positive = binsof (cp_u.w) intersect {1};
bins not_expanded = binsof (cp_u.w) intersect {5};
}
endgroup
covergroup cg_excluded with function sample (bit [6:0] a, bit b);
cp_a: coverpoint a {
bins normal = {[0 : 3]}; ignore_bins ignored = {1}; illegal_bins illegal = {3};
}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins kept = binsof (cp_a.normal) intersect {0};
bins partial = binsof (cp_a.normal) intersect {[1 : 3]};
bins removed_ignore = binsof (cp_a.normal) intersect {1};
bins removed_illegal = binsof (cp_a.normal) intersect {3};
bins removed_union = binsof (cp_a.normal) intersect {1, 3};
bins complement = !binsof (cp_a.normal) intersect {1};
}
endgroup
covergroup cg_excluded_wildcard with function sample (bit [6:0] a, bit b);
cp_a: coverpoint a {
bins normal = {[0 : 15]};
wildcard ignore_bins odds = {7'b??????1};
illegal_bins band = {[4 : 7]};
ignore_bins empty_range = {[9 : 8]};
}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins kept = binsof (cp_a.normal) intersect {2};
bins removed_pattern = binsof (cp_a.normal) intersect {1, 9, 15};
bins removed_range = binsof (cp_a.normal) intersect {[4 : 7]};
bins removed_union = binsof (cp_a.normal) intersect {[3 : 6]};
}
endgroup
covergroup cg_excluded_wide with function sample (wide_t a, bit b);
cp_a: coverpoint a {bins normal = {LOW, HIGH}; ignore_bins ignored = {LOW};}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins kept = binsof (cp_a.normal) intersect {HIGH};
bins removed = binsof (cp_a.normal) intersect {LOW};
}
endgroup
// Keep the large symbolic-exclusion stress for Verilator; Questa construction
// is prohibitively slow at 31 bits, so use the same pattern at seven bits elsewhere.
`ifdef VERILATOR
typedef bit [30:0] excluded_t;
`else
typedef bit [6:0] excluded_t;
`endif
localparam excluded_t EXCLUDED_MAX = '1;
covergroup cg_excluded_many with function sample (excluded_t a, bit b);
cp_a: coverpoint a {
bins whole = {[0 : EXCLUDED_MAX]};
// Only the all-ones value remains; enumerating live prefix subsets is exponential.
`ifdef VERILATOR
wildcard ignore_bins zero_bit = {
(31'bx & ~31'h00000001), (31'bx & ~31'h00000002), (31'bx & ~31'h00000004),
(31'bx & ~31'h00000008), (31'bx & ~31'h00000010), (31'bx & ~31'h00000020),
(31'bx & ~31'h00000040), (31'bx & ~31'h00000080), (31'bx & ~31'h00000100),
(31'bx & ~31'h00000200), (31'bx & ~31'h00000400), (31'bx & ~31'h00000800),
(31'bx & ~31'h00001000), (31'bx & ~31'h00002000), (31'bx & ~31'h00004000),
(31'bx & ~31'h00008000), (31'bx & ~31'h00010000), (31'bx & ~31'h00020000),
(31'bx & ~31'h00040000), (31'bx & ~31'h00080000), (31'bx & ~31'h00100000),
(31'bx & ~31'h00200000), (31'bx & ~31'h00400000), (31'bx & ~31'h00800000),
(31'bx & ~31'h01000000), (31'bx & ~31'h02000000), (31'bx & ~31'h04000000),
(31'bx & ~31'h08000000), (31'bx & ~31'h10000000), (31'bx & ~31'h20000000),
(31'bx & ~31'h40000000)
};
`else
wildcard ignore_bins zero_bit = {
7'b??????0, 7'b?????0?, 7'b????0??, 7'b???0???, 7'b??0????, 7'b?0?????, 7'b0??????
};
`endif
}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{bins kept = binsof (cp_a.whole) intersect {[0 : EXCLUDED_MAX]};}
endgroup
covergroup cg_transition_ignore with function sample (bit a, bit b);
cp_a: coverpoint a {bins seq = (0 => 1); ignore_bins value_only = {1};}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{bins kept = binsof (cp_a.seq) intersect {1};}
endgroup
// State-bin exclusions also apply to exact four-state selections at construction.
covergroup cg_excluded_four_state with function sample (logic [2:0] a, bit b);
cp_a: coverpoint a {
bins states = {3'b001, 3'bx01, 3'bz01};
ignore_bins xstate = {3'bx01};
wildcard ignore_bins other = {3'b1?1};
}
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins kept = binsof (cp_a.states) intersect {3'bz01};
bins numeric = binsof (cp_a.states) intersect {1};
bins removed = binsof (cp_a.states) intersect {3'bx01};
}
endgroup
covergroup cg_registry with function sample (bit a, bit b);
cp_a: coverpoint a;
cp_b: coverpoint b;
selected: cross cp_a, cp_b{
bins off_diagonal = binsof(cp_a) intersect {0} && binsof(cp_b) intersect {1}
|| binsof(cp_a) intersect {1} && binsof(cp_b) intersect {
0
};
}
endgroup
task automatic sample_retired(bit a, bit b);
cg_registry transient = new;
transient.sample(a, b);
endtask
cg_sets sets_cov = new;
cg_partial partial_cov = new;
cg_zero_product zero_product_cov = new;
cg_fixed_words fixed_words_cov = new;
cg_precedence precedence_cov = new;
cg_numeric numeric_cov = new;
cg_transition transition_cov = new;
cg_wildcard wildcard_cov = new;
cg_words words_cov = new;
cg_fast_paths fast_paths_cov = new;
cg_guards guards_cov = new;
cg_hit_words hit_words_cov = new;
cg_four_state four_state_cov = new;
cg_narrow_wildcard narrow_wildcard_cov = new;
cg_excluded excluded_cov = new;
cg_excluded_wildcard excluded_wildcard_cov = new;
cg_excluded_wide excluded_wide_cov = new;
cg_excluded_many excluded_many_cov = new;
cg_transition_ignore transition_ignore_cov = new;
cg_excluded_four_state excluded_four_state_cov = new;
always @(posedge clk) begin
if (cyc == 0) sample_retired(0, 1);
if (cyc == 1) sample_retired(1, 0);
if (cyc == 2) sample_retired(0, 0);
if (cyc < 81) begin
if (cyc == 0) zero_product_cov.sample(0);
if (cyc < 16) fixed_words_cov.sample(4'(cyc % 5 == 4 ? 15 : 4 * (cyc % 5)), 4'(cyc), 1'(cyc));
if (cyc < 24) sets_cov.sample(7'(cyc / 2), 1'(cyc), cyc / 2 != 2);
if (cyc < 4) begin
if (cyc == 0) `checkr(partial_cov.get_inst_coverage(), 0.0);
partial_cov.sample(1'(cyc / 2), 1'(cyc));
case (cyc)
0: begin
`checkr(partial_cov.get_inst_coverage(), 50.0);
end
1: begin
`checkr(partial_cov.get_inst_coverage(), 100.0 * 4.0 / 6.0);
end
2: begin
`checkr(partial_cov.get_inst_coverage(), 100.0 * 5.0 / 6.0);
end
3: begin
`checkr(partial_cov.get_inst_coverage(), 100.0);
end
default: ;
endcase
end
if (cyc < 8) precedence_cov.sample(1'(cyc / 4), 1'(cyc / 2), 1'(cyc));
if (cyc == 8) precedence_cov.sample(1, 0, 0);
if (cyc < 8)
guards_cov.sample(7'(cyc), 65'(cyc / 4) << 64 | 65'(cyc % 2), cyc < 4 ? -7'sd1 : 7'sd0,
3'(cyc % 3));
// Alternate multiword, empty, single-tuple, and automatic-only hit sets.
if (cyc < 12) begin
case (cyc)
0: hit_words_cov.sample(0, 0, 3'b111);
1: hit_words_cov.sample(10, 0, 3'b111);
2: hit_words_cov.sample(1, 1, 3'b000);
3: hit_words_cov.sample(9, 9, 3'b000);
4: hit_words_cov.sample(1, 0, 3'b001);
5: hit_words_cov.sample(0, 1, 3'b100);
6: hit_words_cov.sample(0, 0, 3'b000);
7: hit_words_cov.sample(8, 0, 3'b010);
8: hit_words_cov.sample(0, 9, 3'b100);
9: hit_words_cov.sample(10, 10, 3'b111);
10: hit_words_cov.sample(2, 0, 3'b111);
11: hit_words_cov.sample(1, 1, 3'b101);
default: ;
endcase
end
// Vary guards and include a no-hit sample before covering the remaining tuples.
if (cyc < 8) begin
case (cyc)
0: fast_paths_cov.sample(0, 0, 0, 1, 0, 0);
1: fast_paths_cov.sample(0, 0, 0, 0, 0, 0);
2: fast_paths_cov.sample(0, 4, 0, 1, 1, 1);
3: fast_paths_cov.sample(0, 0, 0, 0, 1, 1);
4: fast_paths_cov.sample(0, 0, 0, 1, 1, 1);
5: fast_paths_cov.sample(0, 0, 0, 1, 1, 0);
6: fast_paths_cov.sample(3, 3, 3, 1, 1, 1);
7: fast_paths_cov.sample(0, 0, 0, 1, 1, 1);
default: ;
endcase
end
else if (cyc < 35) begin
fast_paths_cov.sample(3'(1 + (cyc - 8) / 9), 3'(1 + ((cyc - 8) / 3) % 3),
3'(1 + (cyc - 8) % 3), 1, 1, 1);
end
if (cyc < 6)
numeric_cov.sample(signed_t'(cyc < 2 ? -1 : 2 * (cyc / 2) - 2),
(cyc % 2 != 0) ? HIGH : LOW);
if (cyc < 10) transition_cov.sample(7'(cyc % 5), 1'(cyc / 5));
if (cyc < 4) wildcard_cov.sample(cyc < 2 ? 7'sd1 : 7'sd32, 1'(cyc));
if (cyc < 4) begin
narrow_wildcard_cov.sample(cyc < 2 ? 7'sd1 : -7'sd3, 1'(cyc));
excluded_cov.sample(cyc < 2 ? 7'd0 : 7'd2, 1'(cyc));
excluded_wildcard_cov.sample(cyc < 2 ? 7'd0 : 7'd8, 1'(cyc));
transition_ignore_cov.sample(1'(cyc), 1'(cyc / 2));
end
if (cyc < 2) begin
excluded_wide_cov.sample(HIGH, 1'(cyc));
excluded_many_cov.sample(EXCLUDED_MAX, 1'(cyc));
end
words_cov.sample(7'(cyc / 9), 7'(cyc % 9));
end
else begin
`checkr(sets_cov.get_inst_coverage(), 100.0);
`checkr(partial_cov.get_inst_coverage(), 100.0);
`checkr(zero_product_cov.get_inst_coverage(), 100.0);
`checkr(fixed_words_cov.get_inst_coverage(), 100.0);
`checkr(precedence_cov.get_inst_coverage(), 100.0);
`checkr(numeric_cov.get_inst_coverage(), 100.0);
`checkr(transition_cov.get_inst_coverage(), 100.0);
`checkr(wildcard_cov.get_inst_coverage(), 100.0);
`checkr(words_cov.get_inst_coverage(), 100.0);
`checkr(fast_paths_cov.get_inst_coverage(), 100.0);
`checkr(guards_cov.get_inst_coverage(), 100.0);
`checkr(hit_words_cov.get_inst_coverage(), 100.0);
`checkr(four_state_cov.get_inst_coverage(), 0.0);
`checkr(narrow_wildcard_cov.get_inst_coverage(), 100.0);
`checkr(excluded_cov.get_inst_coverage(), 100.0);
`checkr(excluded_wildcard_cov.get_inst_coverage(), 100.0);
`checkr(excluded_wide_cov.get_inst_coverage(), 100.0);
`checkr(excluded_many_cov.get_inst_coverage(), 100.0);
`checkr(transition_ignore_cov.get_inst_coverage(), 100.0);
`checkr(excluded_four_state_cov.get_inst_coverage(), 0.0);
`ifdef VERILATOR
// Bin counts alone cannot detect leaked live instances. Standard SystemVerilog
// cannot query the registry's live/retired state, so check retirement directly.
`checkd(
$c32(
"Verilated::threadContextp()->covergroupRegistryp()->liveInstanceCount(\"cg_registry\")"),
0);
`checkd($c32(
"Verilated::threadContextp()->covergroupRegistryp()->retiredInstanceCount(\"cg_registry\")"
), 3);
`endif
$write("*-* All Finished *-*\n");
$finish;
end
++cyc;
end
endmodule
+16 -37
View File
@@ -90,50 +90,29 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:162:32: Unsupported: 'with' in cover bin
162 | bins div_by_2_paren[] = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:21: Unsupported: 'binsof' in coverage select expression
168 | bins bin_na = ! binsof(a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:30: Unsupported: 'intersect' in coverage select expression
170 | bins bin_d = binsof(a) intersect { b };
| ^~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:31: Unsupported: 'intersect' in coverage select expression
171 | bins bin_nd = ! binsof(a) intersect { b };
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:20: Unsupported: 'with' in coverage select expression
173 | bins bin_e = with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:20: Unsupported: 'with' in coverage select expression
168 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:24: Unsupported: 'with' in coverage select expression
174 | bins bin_not_e = ! with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:24: Unsupported: 'with' in coverage select expression
169 | bins bin_not_e = ! with (a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:32: Unsupported: '&&' in coverage select expression
176 | bins bin_and = binsof(a) && binsof(b);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:31: Unsupported: '||' in coverage select expression
177 | bins bin_or = binsof(a) || binsof(b);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:33: Unsupported: 'with' in coverage select expression
178 | bins bin_with = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:33: Unsupported: 'with' in coverage select expression
171 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:179:49: Unsupported: 'with' in coverage select expression
179 | bins bin_or_with = binsof(a) || binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:172:49: Unsupported: 'with' in coverage select expression
172 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:179:36: Unsupported: '||' in coverage select expression
179 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:180:50: Unsupported: 'with' in coverage select expression
180 | bins bin_and_with = binsof(a) && binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:50: Unsupported: 'with' in coverage select expression
173 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:180:37: Unsupported: '&&' in coverage select expression
180 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:183:7: Unsupported: explicit coverage cross bins
183 | ignore_bins ib_cross = binsof(a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:7: Unsupported: explicit coverage cross bins
176 | ignore_bins ib_cross = binsof(a);
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:184:7: Unsupported: explicit coverage cross bins
184 | illegal_bins lib_cross = binsof(a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:7: Unsupported: explicit coverage cross bins
177 | illegal_bins lib_cross = binsof(a);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_covergroup_unsup.v:209:5: Unsupported: covergroup inheritance (extends)
209 | covergroup extends cg_empty;
%Error-UNSUPPORTED: t/t_covergroup_unsup.v:202:5: Unsupported: covergroup inheritance (extends)
202 | covergroup extends cg_empty;
| ^~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to
+1 -8
View File
@@ -165,20 +165,13 @@ module t (
covergroup cg_cross_bins;
cross a, b {
bins bin_na = ! binsof(a);
bins bin_d = binsof(a) intersect { b };
bins bin_nd = ! binsof(a) intersect { b };
bins bin_e = with (a);
bins bin_not_e = ! with (a);
bins bin_and = binsof(a) && binsof(b);
bins bin_or = binsof(a) || binsof(b);
bins bin_with = binsof(a) with (a);
bins bin_or_with = binsof(a) || binsof(a) with (a);
bins bin_and_with = binsof(a) && binsof(a) with (a);
bins bin_multiple_fields = binsof(p.inner_packet.field);
bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
// explicit cross ignore/illegal bins (unsupported)
ignore_bins ib_cross = binsof(a);
illegal_bins lib_cross = binsof(a);
+26 -62
View File
@@ -90,47 +90,26 @@
%Warning-COVERIGN: t/t_covergroup_unsup.v:162:32: Unsupported: 'with' in cover bin
162 | bins div_by_2_paren[] = a with (item % 2 == 0);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:21: Unsupported: 'binsof' in coverage select expression
168 | bins bin_na = ! binsof(a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:30: Unsupported: 'intersect' in coverage select expression
170 | bins bin_d = binsof(a) intersect { b };
| ^~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:31: Unsupported: 'intersect' in coverage select expression
171 | bins bin_nd = ! binsof(a) intersect { b };
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:20: Unsupported: 'with' in coverage select expression
173 | bins bin_e = with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:20: Unsupported: 'with' in coverage select expression
168 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:24: Unsupported: 'with' in coverage select expression
174 | bins bin_not_e = ! with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:24: Unsupported: 'with' in coverage select expression
169 | bins bin_not_e = ! with (a);
| ^
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:32: Unsupported: '&&' in coverage select expression
176 | bins bin_and = binsof(a) && binsof(b);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:31: Unsupported: '||' in coverage select expression
177 | bins bin_or = binsof(a) || binsof(b);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:33: Unsupported: 'with' in coverage select expression
178 | bins bin_with = binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:33: Unsupported: 'with' in coverage select expression
171 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:179:49: Unsupported: 'with' in coverage select expression
179 | bins bin_or_with = binsof(a) || binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:172:49: Unsupported: 'with' in coverage select expression
172 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:179:36: Unsupported: '||' in coverage select expression
179 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:180:50: Unsupported: 'with' in coverage select expression
180 | bins bin_and_with = binsof(a) && binsof(a) with (a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:50: Unsupported: 'with' in coverage select expression
173 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:180:37: Unsupported: '&&' in coverage select expression
180 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~
%Warning-COVERIGN: t/t_covergroup_unsup.v:183:7: Unsupported: explicit coverage cross bins
183 | ignore_bins ib_cross = binsof(a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:7: Unsupported: explicit coverage cross bins
176 | ignore_bins ib_cross = binsof(a);
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:184:7: Unsupported: explicit coverage cross bins
184 | illegal_bins lib_cross = binsof(a);
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:7: Unsupported: explicit coverage cross bins
177 | illegal_bins lib_cross = binsof(a);
| ^~~~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:98:18: Ignoring unsupported coverage cross option: 'comment'
98 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; }
@@ -145,40 +124,25 @@
103 | bins one = crossfunc();
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:168:7: Unsupported: explicit coverage cross bins
168 | bins bin_na = ! binsof(a);
168 | bins bin_e = with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:170:7: Unsupported: explicit coverage cross bins
170 | bins bin_d = binsof(a) intersect { b };
%Warning-COVERIGN: t/t_covergroup_unsup.v:169:7: Unsupported: explicit coverage cross bins
169 | bins bin_not_e = ! with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Unsupported: explicit coverage cross bins
171 | bins bin_nd = ! binsof(a) intersect { b };
171 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:172:7: Unsupported: explicit coverage cross bins
172 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:173:7: Unsupported: explicit coverage cross bins
173 | bins bin_e = with (a);
173 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:47: Unsupported: 'binsof' in coverage select expression
174 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:174:7: Unsupported: explicit coverage cross bins
174 | bins bin_not_e = ! with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:7: Unsupported: explicit coverage cross bins
176 | bins bin_and = binsof(a) && binsof(b);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:177:7: Unsupported: explicit coverage cross bins
177 | bins bin_or = binsof(a) || binsof(b);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Unsupported: explicit coverage cross bins
178 | bins bin_with = binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:179:7: Unsupported: explicit coverage cross bins
179 | bins bin_or_with = binsof(a) || binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:180:7: Unsupported: explicit coverage cross bins
180 | bins bin_and_with = binsof(a) && binsof(a) with (a);
| ^~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:181:34: Unsupported: 'binsof' in coverage select expression
181 | bins bin_multiple_fields = binsof(p.inner_packet.field);
| ^~~~~~
%Warning-COVERIGN: t/t_covergroup_unsup.v:181:7: Unsupported: explicit coverage cross bins
181 | bins bin_multiple_fields = binsof(p.inner_packet.field);
174 | bins bin_multiple_fields = binsof(a) && binsof(p.inner_packet.field);
| ^~~~
%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:76:5: Logical operator COVERPOINT 'a' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits.
: ... note: In instance 't'
+17
View File
@@ -954,6 +954,23 @@ module Vt_debug_emitv_t;
bins y1 = {'sh1};
};
cx: cross cp_x, cp_y iff ((cg_sig[0] == cg_sig2[0]));
cx_select: cross cp_x, cp_y {
bins plain = binsof(cp_x);
bins named = binsof(cp_x.x0);
bins filtered = binsof(cp_x) intersect {'sh0, ['sh1:'sh2]};
bins lower = binsof(cp_x) intersect {[$:'sh0]};
bins upper = binsof(cp_x) intersect {['sh1:$]};
bins negated = !binsof(cp_x.x0);
bins complemented = !binsof(cp_x) intersect {'sh0};
bins either = (binsof(cp_x.x0) || binsof(
cp_y.y0));
bins both = (binsof(cp_x.x1) && binsof(
cp_y.y1)) iff (
cg_sig[1]);
bins grouped = ((binsof(cp_x.x0) || binsof(
cp_y.y0))
&& !binsof(cp_x.x1));
}
endfunction
int signed __Vint;
struct {
+12
View File
@@ -434,6 +434,18 @@ module t (/*AUTOARG*/
bins y1 = {1};
}
cx: cross cp_x, cp_y iff (cg_sig[0] == cg_sig2[0]);
cx_select: cross cp_x, cp_y{
bins plain = binsof (cp_x);
bins named = binsof (cp_x.x0);
bins filtered = binsof (cp_x) intersect {0, [1 : 2]};
bins lower = binsof (cp_x) intersect {[$ : 0]};
bins upper = binsof (cp_x) intersect {[1 : $]};
bins negated = !binsof (cp_x.x0);
bins complemented = !binsof (cp_x) intersect {0};
bins either = binsof (cp_x.x0) || binsof (cp_y.y0);
bins both = binsof (cp_x.x1) && binsof (cp_y.y1) iff (cg_sig[1]);
bins grouped = (binsof (cp_x.x0) || binsof (cp_y.y0)) && !binsof (cp_x.x1);
}
endgroup
cg_basic cg_basic_inst = new;