mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Support logic and set operations on binsof (#8306)
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user