Add runtime support for covergroup cross-points (#7847)
This commit is contained in:
parent
e2f7755611
commit
a3553e7bee
|
|
@ -41,9 +41,13 @@ enum class VlCovBinKind : uint8_t {
|
|||
|
||||
//=============================================================================
|
||||
// VlCoverpointIf
|
||||
/// Read-side view of a coverpoint. The writer queries bins by index; the
|
||||
/// implementor computes names/kinds on demand. Bounded bin count, so random
|
||||
/// access by index is the primary usage.
|
||||
/// Read-side view of a coverpoint -- a named, index-addressable set of bins
|
||||
/// with a coverage fraction. A cross is also a coverpoint from this view: its
|
||||
/// auto cross bins (one per element of the Cartesian product of the feeding
|
||||
/// coverpoints' Normal bins) are all Normal, and their names are built on
|
||||
/// demand by concatenating the feeding coverpoints' bin names. The writer
|
||||
/// queries bins by index; the implementor computes names/kinds on demand.
|
||||
/// Bounded bin count, so random access by index is the primary usage.
|
||||
|
||||
class VlCoverpointIf VL_NOT_FINAL {
|
||||
public:
|
||||
|
|
@ -51,11 +55,11 @@ public:
|
|||
virtual ~VlCoverpointIf() = default;
|
||||
|
||||
// METHODS
|
||||
// All bins, across every set; index range [0, binCount())
|
||||
virtual int binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]")
|
||||
virtual std::string binName(int i) const = 0;
|
||||
virtual VlCovBinKind binKind(int i) const = 0;
|
||||
// All bins, across every set; index range [0, binCount()).
|
||||
virtual uint32_t binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]"); for a cross,
|
||||
// the concatenated cross bin name (e.g. "b1_x_b2_x_b3")
|
||||
virtual std::string binName(uint32_t i) const = 0;
|
||||
// Bins covered / effective total (Normal set only) for the coverage calc
|
||||
virtual void coverageParts(double& covered, double& total) const = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,34 +23,57 @@
|
|||
|
||||
#include "verilated_covergroup.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
// This file is compiled whenever covergroups are used, with or without
|
||||
// "verilator --coverage" (see V3Global::verilatedCppFiles). Bin counts are
|
||||
// members of the covergroup objects themselves, so sampling, bin naming, and
|
||||
// coverage queries such as get_inst_coverage() all work with no coverage
|
||||
// database present. VL_COVER_INSERT does not copy a count; it hands the
|
||||
// database the address of a counter to read at write time. Only that
|
||||
// publication step needs verilated_cov.cpp, which is compiled solely under
|
||||
// --coverage, so only the registerBins() bodies are gated on VM_COVERAGE.
|
||||
#if VM_COVERAGE
|
||||
#include "verilated_cov.h"
|
||||
#endif
|
||||
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) {
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, uint32_t nBins) {
|
||||
m_hier = hier;
|
||||
m_atLeast = atLeast;
|
||||
m_total = nBins;
|
||||
m_counts.assign(nBins, 0);
|
||||
m_crossIdx.assign(nBins, -1);
|
||||
m_crossToBin.clear();
|
||||
}
|
||||
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col) {
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col) {
|
||||
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, col);
|
||||
if (set == VlCovBinKind::KIND_NORMAL) {
|
||||
// Assign each Normal bin a cross index, and record the inverse map.
|
||||
for (uint32_t b = m_nextBase; b < m_nextBase + count; ++b) {
|
||||
m_crossIdx[b] = static_cast<int>(m_crossToBin.size());
|
||||
m_crossToBin.push_back(b);
|
||||
}
|
||||
m_normal += count;
|
||||
}
|
||||
m_nextBase += count;
|
||||
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
|
||||
// Namers are appended in ascending, contiguous index order covering [0, m_total),
|
||||
// and i is always a valid bin index, so the matching namer always exists.
|
||||
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
|
||||
// Build the bin name based on the bin index
|
||||
return binName(m_crossToBin[crossIdx]);
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
|
||||
// Namers are appended in ascending order covering [0, m_total),
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (i < nm.base() + nm.count()) return nm;
|
||||
}
|
||||
VL_UNREACHABLE;
|
||||
VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
std::string VlCoverpoint::binName(int i) const {
|
||||
std::string VlCoverpoint::binName(uint32_t i) const {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
std::string name = nm.name();
|
||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
||||
|
|
@ -59,7 +82,7 @@ std::string VlCoverpoint::binName(int i) const {
|
|||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
for (int i = 0; i < binCount(); ++i) {
|
||||
for (uint32_t i = 0; i < binCount(); ++i) {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
const VlCovBinKind kind = binKind(i);
|
||||
const std::string binp = binName(i);
|
||||
|
|
@ -81,3 +104,89 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
|||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
|
||||
void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cps,
|
||||
const char* file, int line, int col) {
|
||||
m_hier = hier;
|
||||
m_file = file;
|
||||
m_line = line;
|
||||
m_col = col;
|
||||
m_dims = dims;
|
||||
m_cps.assign(cps, cps + dims);
|
||||
m_cpBinCounts.resize(dims);
|
||||
// Accumulate in 64 bits so the overflow check itself cannot overflow.
|
||||
uint64_t product = 1;
|
||||
for (uint32_t d = 0; d < dims; ++d) {
|
||||
m_cpBinCounts[d] = cps[d]->normalBinCount();
|
||||
product *= m_cpBinCounts[d];
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
|
||||
void VlCoverCross::iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx) {
|
||||
const uint32_t hits = cps[dim]->hitCount();
|
||||
const uint32_t* const list = cps[dim]->hitList();
|
||||
const bool last = (dim == m_dims - 1);
|
||||
const uint32_t stride = m_stride[dim];
|
||||
for (uint32_t hit = 0; hit < hits; ++hit) {
|
||||
const uint32_t idx = baseIdx + list[hit] * stride;
|
||||
if (last) {
|
||||
incrementTuple(idx);
|
||||
} else {
|
||||
iterateProduct(cps, dim + 1, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCross::sample(VlCoverpoint* const* cps) {
|
||||
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
if (cps[d]->hitCount() == 0) return;
|
||||
}
|
||||
iterateProduct(cps, 0, 0);
|
||||
}
|
||||
|
||||
std::string VlCoverCross::binName(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];
|
||||
if (d > 0) name += "_x_";
|
||||
name += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
// Register every auto cross bin (zero-count bins included), so the report
|
||||
// shows the full Cartesian product of cross bins. Names are built on the fly.
|
||||
const std::string lineStr = std::to_string(m_line);
|
||||
const std::string colStr = std::to_string(m_col);
|
||||
for (uint32_t flat = 0; flat < binCount(); ++flat) {
|
||||
const std::string bin = binName(flat); // "b1_x_b2_x_..."
|
||||
// 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];
|
||||
if (d > 0) crossBins += ",";
|
||||
crossBins += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
const std::string full = m_hier + "." + bin;
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCounts[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());
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
||||
/// and registers via registerBins().
|
||||
///
|
||||
/// Collection and coverage queries are always available; only registerBins(),
|
||||
/// which publishes bin counters to the coverage database, requires VM_COVERAGE.
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
|
||||
|
|
@ -49,8 +52,8 @@ enum class VlCovBinNaming : uint8_t {
|
|||
class VlCovNamer final {
|
||||
// MEMBERS
|
||||
VlCovBinKind m_set; // which set the bins belong to
|
||||
int m_count; // bins this namer covers (1 for Single)
|
||||
int m_base; // first bin index (declaration order), assigned on append
|
||||
uint32_t m_count; // bins this namer covers (1 for Single)
|
||||
uint32_t m_base; // first bin index (declaration order), assigned on append
|
||||
VlCovBinNaming m_naming; // how bin names are built
|
||||
const char* m_name; // bin name (Single) or array base name (Array)
|
||||
const char* m_file; // declaration file
|
||||
|
|
@ -59,8 +62,8 @@ class VlCovNamer final {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col)
|
||||
VlCovNamer(VlCovBinKind set, uint32_t count, uint32_t base, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col)
|
||||
: m_set{set}
|
||||
, m_count{count}
|
||||
, m_base{base}
|
||||
|
|
@ -72,8 +75,8 @@ public:
|
|||
|
||||
// METHODS
|
||||
VlCovBinKind set() const { return m_set; }
|
||||
int count() const { return m_count; }
|
||||
int base() const { return m_base; }
|
||||
uint32_t count() const { return m_count; }
|
||||
uint32_t base() const { return m_base; }
|
||||
VlCovBinNaming naming() const { return m_naming; }
|
||||
const char* name() const { return m_name; }
|
||||
const char* file() const { return m_file; }
|
||||
|
|
@ -87,19 +90,30 @@ public:
|
|||
/// bin's set/name come from the owning namer. coverage() is computed on demand
|
||||
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
|
||||
|
||||
class VlCoverpoint final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
// Base coverpoint runtime (read side + collection logic, no hit-list storage).
|
||||
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
|
||||
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
|
||||
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
|
||||
protected:
|
||||
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
|
||||
std::string m_hier; // "covergroup.coverpoint"
|
||||
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
||||
int m_total = 0; // bins across all sets
|
||||
int m_normal = 0; // Normal bins (coverage denominator)
|
||||
int m_nextBase = 0; // running append cursor
|
||||
uint32_t m_total = 0; // bins across all sets
|
||||
uint32_t m_normal = 0; // Normal bins (coverage denominator)
|
||||
uint32_t m_nextBase = 0; // running append cursor
|
||||
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
||||
std::vector<VlCovNamer> m_namers; // appended in declaration order
|
||||
// [m_total] full bin idx -> cross idx (Normal-only), -1 otherwise. The only
|
||||
// signed index here: -1 marks a non-Normal bin, which incrementBin filters on.
|
||||
std::vector<int> m_crossIdx;
|
||||
// [m_normal] inverse of m_crossIdx: cross idx -> full bin idx, appended in cross-index order
|
||||
std::vector<uint32_t> m_crossToBin;
|
||||
uint32_t m_hitCount = 0; // entries valid in the hit list this sample
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col);
|
||||
|
||||
public:
|
||||
|
|
@ -108,31 +122,44 @@ public:
|
|||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
void init(const char* hier, uint32_t atLeast, int nBins);
|
||||
void init(const char* hier, uint32_t atLeast, uint32_t nBins);
|
||||
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
|
||||
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
||||
}
|
||||
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
|
||||
int col) {
|
||||
void addArrayNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample()) ----
|
||||
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
|
||||
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
|
||||
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
|
||||
void clearHitList() { m_hitCount = 0; }
|
||||
// Ignore/Illegal/Default: count only; never propagates to cross coverage.
|
||||
void recordHit(uint32_t i) { ++m_counts[i]; }
|
||||
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
|
||||
// where MaxHits is the gen-time max per-sample bin overlap.
|
||||
|
||||
// ---- cross support (read by VlCoverCross) ----
|
||||
uint32_t hitCount() const { return m_hitCount; }
|
||||
virtual const uint32_t* hitList() const = 0; // provided by VlCoverpointT
|
||||
uint32_t normalBinCount() const { return m_normal; } // cross dimension size (Normal bins)
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
int binCount() const override { return m_total; }
|
||||
std::string binName(int i) const override;
|
||||
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
std::string binName(uint32_t i) const override;
|
||||
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
|
||||
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
|
||||
// interface omits it; add it back only if a writer needs it polymorphically.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||
// path (incrementBin) stays a plain counter bump.
|
||||
int numCovered = 0;
|
||||
uint32_t numCovered = 0;
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (int i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
}
|
||||
|
|
@ -141,4 +168,90 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverpointT
|
||||
/// Concrete coverpoint with an inline hit-list array sized to MaxHits -- the
|
||||
/// gen-time maximum number of Normal bins one sample value can match (1 for the
|
||||
/// common non-overlapping case). The bound is a compile-time constant, so for
|
||||
/// MaxHits == 1 incrementBin collapses to a single store. Generated code holds
|
||||
/// the coverpoint as VlCoverpointT<K> and calls incrementBin via the concrete
|
||||
/// type; the cross reads it polymorphically through VlCoverpoint*.
|
||||
|
||||
template <uint32_t MaxHits>
|
||||
class VlCoverpointT final : public VlCoverpoint {
|
||||
// MEMBERS
|
||||
uint32_t m_hits[MaxHits]; // cross indices of Normal bins hit this sample
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverpointT() = default;
|
||||
|
||||
// METHODS
|
||||
// Normal bin: bump count and append the bin's cross index to the hit list.
|
||||
// m_hitCount can never exceed MaxHits (the gen-time overlap bound), so no hit
|
||||
// is ever dropped; the bound check is a compile-time-folded safety net.
|
||||
void incrementBin(uint32_t i) {
|
||||
++m_counts[i];
|
||||
// m_crossIdx is signed only to carry the -1 "not a Normal bin" marker;
|
||||
// the >= 0 test below is what makes every stored hit index unsigned-safe.
|
||||
const int cx = m_crossIdx[i];
|
||||
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
|
||||
}
|
||||
const uint32_t* hitList() const override { return m_hits; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
/// Per-instance auto 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 from the coverpoints, so no per-bin name is stored.
|
||||
|
||||
class VlCoverCross final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
std::string m_hier; // "covergroup.cross"
|
||||
const char* m_file = nullptr; // Cross declaration file (registration metadata)
|
||||
int m_line = 0; // Cross declaration line
|
||||
int m_col = 0; // Cross declaration column
|
||||
uint32_t m_dims = 0; // Number of feeding coverpoints
|
||||
// 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.
|
||||
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 (the only name source)
|
||||
|
||||
// PRIVATE METHODS
|
||||
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
|
||||
void incrementTuple(uint32_t idx) {
|
||||
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCross() = default;
|
||||
|
||||
// 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);
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
void sample(VlCoverpoint* const* cps);
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
|
||||
uint32_t binCount() const override { return m_numAutoBins; }
|
||||
std::string binName(uint32_t flat) const override;
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
covered = m_numCovered;
|
||||
total = m_numAutoBins;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
1078
src/V3Covergroup.cpp
1078
src/V3Covergroup.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -22,7 +22,6 @@ cg7.cp.rev[0]: 1
|
|||
cg7.cp.rev[1]: 0
|
||||
cg8.cp.w[0]: 0
|
||||
cg8.cp.w[1]: 1
|
||||
cg9.__cross7.cumulative_x_lo [cross]: 1
|
||||
cg9.__cross7.ok_x_lo [cross]: 1
|
||||
cg9.cpA.ok: 1
|
||||
cg9.cpB.lo: 1
|
||||
|
|
|
|||
|
|
@ -85,4 +85,28 @@
|
|||
: ... note: In instance 't'
|
||||
73 | bins b_huge[] = {[0:$]};
|
||||
| ^~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:84:41: Non-constant expression in bin range; values must be constants
|
||||
: ... 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
|
||||
: ... note: In instance 't'
|
||||
89 | 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
|
||||
: ... note: In instance 't'
|
||||
94 | 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
|
||||
: ... note: In instance 't'
|
||||
104 | cp_a: coverpoint cp_expr {bins x[] = {[4'h0 : 4'b000x]};}
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:109:36: Non-constant expression in array bins value list; values must be constants
|
||||
: ... note: In instance 't'
|
||||
109 | cp_a: coverpoint cp_expr {bins x[] = {size_var};}
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -74,6 +74,48 @@ module t;
|
|||
}
|
||||
endgroup
|
||||
|
||||
// Malformed bins on a coverpoint that feeds a *cross*. The cross path
|
||||
// sizes the coverpoint's hit list (computeHitListBound/extractRangeIntervals)
|
||||
// before the bin condition is built, so the malformed-bin guards there run gracefully and
|
||||
// the user error is then diagnosed downstream by the bin-condition / array-value builders.
|
||||
// (Without a cross the same errors fire via cg3/cg5 above; the cross also exercises the
|
||||
// hit-list-sizing guard path.)
|
||||
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;
|
||||
endgroup
|
||||
covergroup cgx_nc_range_lo; // non-constant low bound, non-array range
|
||||
cp_a: coverpoint cp_expr {bins x = {[size_var : 1]};}
|
||||
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
|
||||
xc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
covergroup cgx_nc_range_hi; // non-constant high bound, non-array range
|
||||
cp_a: coverpoint cp_expr {bins x = {[0 : size_var]};}
|
||||
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
|
||||
xc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
covergroup cgx_arr_4state_lo; // four-state low bound, array range
|
||||
cp_a: coverpoint cp_expr {bins x[] = {[4'b000x : 4'hF]};}
|
||||
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
|
||||
xc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
covergroup cgx_arr_4state_hi; // four-state high bound, array range
|
||||
cp_a: coverpoint cp_expr {bins x[] = {[4'h0 : 4'b000x]};}
|
||||
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
|
||||
xc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
covergroup cgx_arr_ncval; // non-constant value, array value list
|
||||
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;
|
||||
endgroup
|
||||
covergroup cgx_arr_open; // open-ended ('$') bounds, array range
|
||||
cp_a: coverpoint cp_expr {bins x[] = {[2 : $], [$ : 1]};}
|
||||
cp_c: coverpoint cp_expr {bins r = {0}; bins w = {1};}
|
||||
xc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
|
||||
cg1 cg1_inst = new;
|
||||
cg2 cg2_inst = new;
|
||||
cg2b cg2b_inst = new;
|
||||
|
|
@ -81,6 +123,13 @@ module t;
|
|||
cg4 cg4_inst = new;
|
||||
cg5 cg5_inst = new;
|
||||
cg6 cg6_inst = new;
|
||||
cgx_nc_value cgx_nc_value_inst = new;
|
||||
cgx_nc_range_lo cgx_nc_range_lo_inst = new;
|
||||
cgx_nc_range_hi cgx_nc_range_hi_inst = new;
|
||||
cgx_arr_4state_lo cgx_arr_4state_lo_inst = new;
|
||||
cgx_arr_4state_hi cgx_arr_4state_hi_inst = new;
|
||||
cgx_arr_ncval cgx_arr_ncval_inst = new;
|
||||
cgx_arr_open cgx_arr_open_inst = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -57,6 +57,27 @@ cg5.cp_addr.addr0: 1
|
|||
cg5.cp_addr.addr1: 1
|
||||
cg5.cp_cmd.read: 1
|
||||
cg5.cp_cmd.write: 1
|
||||
cg_arr_4state.a4.av[0]_x_read [cross]: 1
|
||||
cg_arr_4state.a4.av[0]_x_write [cross]: 1
|
||||
cg_arr_4state.cp_addr.av[0]: 2
|
||||
cg_arr_4state.cp_cmd.read: 1
|
||||
cg_arr_4state.cp_cmd.write: 1
|
||||
cg_arr_range.ar.av[0]_x_read [cross]: 1
|
||||
cg_arr_range.ar.av[0]_x_write [cross]: 1
|
||||
cg_arr_range.ar.av[1]_x_read [cross]: 1
|
||||
cg_arr_range.ar.av[1]_x_write [cross]: 1
|
||||
cg_arr_range.cp_addr.av[0]: 2
|
||||
cg_arr_range.cp_addr.av[1]: 2
|
||||
cg_arr_range.cp_cmd.read: 2
|
||||
cg_arr_range.cp_cmd.write: 2
|
||||
cg_arr_vals.av.av[0]_x_read [cross]: 1
|
||||
cg_arr_vals.av.av[0]_x_write [cross]: 1
|
||||
cg_arr_vals.av.av[1]_x_read [cross]: 1
|
||||
cg_arr_vals.av.av[1]_x_write [cross]: 1
|
||||
cg_arr_vals.cp_addr.av[0]: 2
|
||||
cg_arr_vals.cp_addr.av[1]: 2
|
||||
cg_arr_vals.cp_cmd.read: 2
|
||||
cg_arr_vals.cp_cmd.write: 2
|
||||
cg_at_least.addr_cmd_al.addr0_x_read [cross]: 1
|
||||
cg_at_least.addr_cmd_al.addr0_x_write [cross]: 0
|
||||
cg_at_least.addr_cmd_al.addr1_x_read [cross]: 0
|
||||
|
|
@ -65,13 +86,29 @@ cg_at_least.cp_addr.addr0: 1
|
|||
cg_at_least.cp_addr.addr1: 1
|
||||
cg_at_least.cp_cmd.read: 1
|
||||
cg_at_least.cp_cmd.write: 1
|
||||
cg_be.cp_addr.hi: 2
|
||||
cg_be.cp_addr.lo: 2
|
||||
cg_be.cp_cmd.read: 2
|
||||
cg_be.cp_cmd.write: 2
|
||||
cg_be.x.hi_x_read [cross]: 1
|
||||
cg_be.x.hi_x_write [cross]: 1
|
||||
cg_be.x.lo_x_read [cross]: 1
|
||||
cg_be.x.lo_x_write [cross]: 1
|
||||
cg_be_arr.ax.av[0]_x_read [cross]: 1
|
||||
cg_be_arr.ax.av[0]_x_write [cross]: 1
|
||||
cg_be_arr.ax.av[1]_x_read [cross]: 1
|
||||
cg_be_arr.ax.av[1]_x_write [cross]: 1
|
||||
cg_be_arr.cp_addr.av[0]: 2
|
||||
cg_be_arr.cp_addr.av[1]: 2
|
||||
cg_be_arr.cp_cmd.read: 2
|
||||
cg_be_arr.cp_cmd.write: 2
|
||||
cg_def_cross.axc.a0_x_read [cross]: 1
|
||||
cg_def_cross.axc.a0_x_write [cross]: 0
|
||||
cg_def_cross.axc.a1_x_read [cross]: 0
|
||||
cg_def_cross.axc.a1_x_write [cross]: 0
|
||||
cg_def_cross.cp_a.a0: 1
|
||||
cg_def_cross.cp_a.a1: 0
|
||||
cg_def_cross.cp_a.ad: 1
|
||||
cg_def_cross.cp_a.ad [default]: 1
|
||||
cg_def_cross.cp_c.read: 1
|
||||
cg_def_cross.cp_c.write: 1
|
||||
cg_goal.addr_cmd_goal.addr0_x_read [cross]: 1
|
||||
|
|
@ -91,6 +128,11 @@ cg_ignore.cross_ab.a0_x_read [cross]: 1
|
|||
cg_ignore.cross_ab.a0_x_write [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_write [cross]: 1
|
||||
cg_inv.cp_addr.inv: 0
|
||||
cg_inv.cp_cmd.read: 1
|
||||
cg_inv.cp_cmd.write: 1
|
||||
cg_inv.iv.inv_x_read [cross]: 0
|
||||
cg_inv.iv.inv_x_write [cross]: 0
|
||||
cg_mixed.ab.addr0_x_read [cross]: 1
|
||||
cg_mixed.ab.addr0_x_write [cross]: 1
|
||||
cg_mixed.ab.addr1_x_read [cross]: 1
|
||||
|
|
@ -101,6 +143,25 @@ cg_mixed.cp_cmd.read: 2
|
|||
cg_mixed.cp_cmd.write: 2
|
||||
cg_mixed.cp_solo.debug: 2
|
||||
cg_mixed.cp_solo.normal: 2
|
||||
cg_noNormal.cp_addr.ig [ignore]: 2
|
||||
cg_noNormal.cp_cmd.read: 1
|
||||
cg_noNormal.cp_cmd.write: 1
|
||||
cg_openrange.cp_addr.hi: 2
|
||||
cg_openrange.cp_addr.lo: 2
|
||||
cg_openrange.cp_cmd.read: 2
|
||||
cg_openrange.cp_cmd.write: 2
|
||||
cg_openrange.orc.hi_x_read [cross]: 1
|
||||
cg_openrange.orc.hi_x_write [cross]: 1
|
||||
cg_openrange.orc.lo_x_read [cross]: 1
|
||||
cg_openrange.orc.lo_x_write [cross]: 1
|
||||
cg_overlap.cp_addr.hi: 3
|
||||
cg_overlap.cp_addr.lo: 3
|
||||
cg_overlap.cp_cmd.read: 3
|
||||
cg_overlap.cp_cmd.write: 2
|
||||
cg_overlap.ov.hi_x_read [cross]: 2
|
||||
cg_overlap.ov.hi_x_write [cross]: 1
|
||||
cg_overlap.ov.lo_x_read [cross]: 2
|
||||
cg_overlap.ov.lo_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_read [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.lo_range_x_read [cross]: 1
|
||||
|
|
@ -109,6 +170,11 @@ cg_range.cp_addr.hi_range: 2
|
|||
cg_range.cp_addr.lo_range: 2
|
||||
cg_range.cp_cmd.read: 2
|
||||
cg_range.cp_cmd.write: 2
|
||||
cg_trans.cp_t.t01: 1
|
||||
cg_trans.cp_v.v5: 2
|
||||
cg_trans.cp_v.v6: 1
|
||||
cg_trans.tx.t01_x_v5 [cross]: 1
|
||||
cg_trans.tx.t01_x_v6 [cross]: 0
|
||||
cg_unnamed_cross.__cross8.a0_x_read [cross]: 1
|
||||
cg_unnamed_cross.__cross8.a0_x_write [cross]: 0
|
||||
cg_unnamed_cross.__cross8.a1_x_read [cross]: 0
|
||||
|
|
@ -125,3 +191,21 @@ cg_unsup_cross_opt.cp_addr.addr0: 1
|
|||
cg_unsup_cross_opt.cp_addr.addr1: 1
|
||||
cg_unsup_cross_opt.cp_cmd.read: 1
|
||||
cg_unsup_cross_opt.cp_cmd.write: 1
|
||||
cg_wide.cp_cmd.read: 2
|
||||
cg_wide.cp_cmd.write: 2
|
||||
cg_wide.cp_wide.hi: 2
|
||||
cg_wide.cp_wide.lo: 2
|
||||
cg_wide.wd.hi_x_read [cross]: 1
|
||||
cg_wide.wd.hi_x_write [cross]: 1
|
||||
cg_wide.wd.lo_x_read [cross]: 1
|
||||
cg_wide.wd.lo_x_write [cross]: 1
|
||||
cg_wild_arr.cp_addr.wb: 4
|
||||
cg_wild_arr.cp_cmd.read: 2
|
||||
cg_wild_arr.cp_cmd.write: 2
|
||||
cg_wild_arr.wa.wb_x_read [cross]: 2
|
||||
cg_wild_arr.wa.wb_x_write [cross]: 2
|
||||
cg_wild_solo.cp_addr.wb: 2
|
||||
cg_wild_solo.cp_cmd.read: 1
|
||||
cg_wild_solo.cp_cmd.write: 1
|
||||
cg_wild_solo.ws.wb_x_read [cross]: 1
|
||||
cg_wild_solo.ws.wb_x_write [cross]: 1
|
||||
|
|
|
|||
|
|
@ -16,6 +16,18 @@ module t;
|
|||
logic cmd;
|
||||
logic mode;
|
||||
logic parity;
|
||||
logic [63:0] wide;
|
||||
logic [3:0] state;
|
||||
logic [3:0] val;
|
||||
|
||||
// Ascending ("opposite-endian") bit ranges: bit 0 is the MSB. Bit ordering must
|
||||
// not change coverage results: range bins operate on the sampled value, so an
|
||||
// ascending-declared coverpoint produces identical hit counts to the usual
|
||||
// descending declaration.
|
||||
/* verilator lint_off ASCRANGE */
|
||||
logic [0:1] be_addr;
|
||||
logic [0:0] be_cmd;
|
||||
/* verilator lint_on ASCRANGE */
|
||||
|
||||
typedef struct packed {logic m_p; logic h_mode;} cfg_t;
|
||||
cfg_t s_cfg = '0;
|
||||
|
|
@ -108,8 +120,8 @@ module t;
|
|||
cross cp_a, cp_c; // no label: reported under the default cross name
|
||||
endgroup
|
||||
|
||||
// Cross plus an un-crossed coverpoint: get_inst_coverage must combine the converted
|
||||
// (VlCoverpoint) coverpoint cp_solo with the legacy cross/crossed-coverpoint bins.
|
||||
// Cross plus an un-crossed coverpoint: get_inst_coverage must combine the un-crossed
|
||||
// coverpoint cp_solo with the cross and its crossed coverpoints.
|
||||
covergroup cg_mixed;
|
||||
cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
|
|
@ -117,14 +129,125 @@ module t;
|
|||
ab: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed (hence non-convertible) coverpoint that also has a default bin: exercises the
|
||||
// legacy default-bin codegen path that converted coverpoints bypass.
|
||||
// Crossed coverpoint that also has a default bin: exercises default-bin handling on a
|
||||
// coverpoint that feeds a cross.
|
||||
covergroup cg_def_cross;
|
||||
cp_a: coverpoint addr iff (mode) {bins a0 = {0}; bins a1 = {1}; bins ad = default;}
|
||||
cp_c: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
axc: cross cp_a, cp_c;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an array range bin (bins av[] = {[0:1]}): exercises the
|
||||
// coverpoint hit-list sizing for array range elements feeding a cross.
|
||||
covergroup cg_arr_range;
|
||||
cp_addr: coverpoint addr {bins av[] = {[0 : 1]};} // -> av[0]=0, av[1]=1
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
ar: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an array value bin (bins av[] = {0, 2}): array value elements.
|
||||
covergroup cg_arr_vals;
|
||||
cp_addr: coverpoint addr {bins av[] = {0, 2};} // -> av[0]=0, av[1]=2
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
av: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a wildcard array bin (wildcard bins wb[] = {2'b0?}): the '0?'
|
||||
// wildcard expands to addr values 0 and 1, each its own array element.
|
||||
covergroup cg_wild_arr;
|
||||
cp_addr: coverpoint addr {wildcard bins wb[] = {2'b0?};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
wa: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a non-array wildcard bin (wildcard bins wb = {2'b0?}): single
|
||||
// wildcard bin matching addr 0 or 1, feeding a cross.
|
||||
covergroup cg_wild_solo;
|
||||
cp_addr: coverpoint addr {wildcard bins wb = {2'b0?};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
ws: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a four-state literal in a non-wildcard array bin
|
||||
// (bins av[] = {2'b0x}): LRM 1800-2023 19.5.4 permits 4-state values in a bin definition.
|
||||
// The hit-list sizing cannot statically analyze a 4-state value, so it falls back to the
|
||||
// safe slot count. Under Verilator's 2-state simulation the value matches addr=0.
|
||||
covergroup cg_arr_4state;
|
||||
cp_addr: coverpoint addr {bins av[] = {2'b0x};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
a4: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with two *overlapping* Normal range bins (addr=1 is in both lo and
|
||||
// hi): the hit-list sizing must report a bound > 1 (a single sample can fall in two bins),
|
||||
// exercising the max-overlap computation in computeHitListBound.
|
||||
covergroup cg_overlap;
|
||||
cp_addr: coverpoint addr {bins lo = {[0 : 1]}; bins hi = {[1 : 2]};} // overlap at addr=1
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
ov: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint whose sampled expression is wider than 64 bits: exercises the
|
||||
// width>=64 max-value path in the hit-list sizing (where 1<<width would overflow).
|
||||
covergroup cg_wide;
|
||||
cp_wide: coverpoint wide {bins lo = {[0 : 1]}; bins hi = {[2 : 3]};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
wd: cross cp_wide, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with open-ended ('$') range bins: 'lo' is open-low ([$:1]) and 'hi'
|
||||
// is open-high ([2:$]); exercises the unbounded-bound interval handling in the hit-list
|
||||
// sizing (lo clamps to 0, hi clamps to the type max).
|
||||
covergroup cg_openrange;
|
||||
cp_addr: coverpoint addr {bins lo = {[$ : 1]}; bins hi = {[2 : $]};}
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
orc: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the bin matches no
|
||||
// value, so the hit-list sizing rejects it (lo > hi) and falls back to the safe slot count.
|
||||
// The 'inv' bin and its cross bins are therefore never hit (coverage stays at 40%).
|
||||
covergroup cg_inv;
|
||||
cp_addr: coverpoint addr {bins inv = {[3 : 0]};} // inverted -> never matches
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
iv: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with *no* Normal bins (only ignore_bins): the cross has an empty bin
|
||||
// product, so the hit-list sizing returns the safe bound of 1. Coverage is the cmd
|
||||
// coverpoint alone (vacuously 100% once both cmd bins are hit).
|
||||
covergroup cg_noNormal;
|
||||
cp_addr: coverpoint addr {ignore_bins ig = {[0 : 3]};} // zero Normal bins
|
||||
cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
nn: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross of a *transition* coverpoint with a value coverpoint. Transition coverpoints route
|
||||
// through the VlCoverpoint runtime (their completion appends to the hit list), so a cross
|
||||
// can read them like any other coverpoint.
|
||||
covergroup cg_trans;
|
||||
cp_t: coverpoint state {bins t01 = (0 => 1);} // one Normal (transition) bin
|
||||
cp_v: coverpoint val {bins v5 = {5}; bins v6 = {6};}
|
||||
tx: cross cp_t, cp_v;
|
||||
endgroup
|
||||
|
||||
// Range bins over an ascending-declared coverpoint, crossed with another: the
|
||||
// range interval extraction must ignore bit endianness (mirror of cg_range).
|
||||
covergroup cg_be;
|
||||
cp_addr: coverpoint be_addr {bins lo = {[0 : 1]}; bins hi = {[2 : 3]};}
|
||||
cp_cmd: coverpoint be_cmd {bins read = {0}; bins write = {1};}
|
||||
x: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Array range bin over an ascending-declared coverpoint: {[0:1]} enumerates to
|
||||
// av[0]=0, av[1]=1, exercising array-range slot enumeration under opposite
|
||||
// endian (mirror of cg_arr_range).
|
||||
covergroup cg_be_arr;
|
||||
cp_addr: coverpoint be_addr {bins av[] = {[0 : 1]};}
|
||||
cp_cmd: coverpoint be_cmd {bins read = {0}; bins write = {1};}
|
||||
ax: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
cg2 cg2_inst = new;
|
||||
cg_ignore cg_ignore_inst = new;
|
||||
cg_range cg_range_inst = new;
|
||||
|
|
@ -137,6 +260,19 @@ module t;
|
|||
cg_unnamed_cross cg_unnamed_cross_inst = new;
|
||||
cg_mixed cg_mixed_inst = new;
|
||||
cg_def_cross cg_def_cross_inst = new;
|
||||
cg_arr_range cg_arr_range_inst = new;
|
||||
cg_arr_vals cg_arr_vals_inst = new;
|
||||
cg_wild_arr cg_wild_arr_inst = new;
|
||||
cg_wild_solo cg_wild_solo_inst = new;
|
||||
cg_arr_4state cg_arr_4state_inst = new;
|
||||
cg_overlap cg_overlap_inst = new;
|
||||
cg_wide cg_wide_inst = new;
|
||||
cg_openrange cg_openrange_inst = new;
|
||||
cg_inv cg_inv_inst = new;
|
||||
cg_noNormal cg_noNormal_inst = new;
|
||||
cg_trans cg_trans_inst = new;
|
||||
cg_be cg_be_inst = new;
|
||||
cg_be_arr cg_be_arr_inst = new;
|
||||
|
||||
initial begin
|
||||
// Sample 2-way: hit all 4 combinations
|
||||
|
|
@ -320,6 +456,106 @@ module t;
|
|||
addr = 0; cmd = 0; cg_def_cross_inst.sample(); // a0, read
|
||||
addr = 2; cmd = 1; cg_def_cross_inst.sample(); // ad (default), write
|
||||
|
||||
// Sample cg_arr_range: array range bin {[0:1]} -> av[0]=0, av[1]=1; cross 2x2
|
||||
// cg_arr_range: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
addr = 0; cmd = 0; cg_arr_range_inst.sample(); // av[0] x read
|
||||
addr = 0; cmd = 1; cg_arr_range_inst.sample(); // av[0] x write
|
||||
addr = 1; cmd = 0; cg_arr_range_inst.sample(); // av[1] x read
|
||||
addr = 1; cmd = 1; cg_arr_range_inst.sample(); // av[1] x write
|
||||
`checkr(cg_arr_range_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_arr_vals: array value bin {0,2} -> av[0]=0, av[1]=2; cross 2x2
|
||||
// cg_arr_vals: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
addr = 0; cmd = 0; cg_arr_vals_inst.sample(); // av[0] x read
|
||||
addr = 0; cmd = 1; cg_arr_vals_inst.sample(); // av[0] x write
|
||||
addr = 2; cmd = 0; cg_arr_vals_inst.sample(); // av[1] x read
|
||||
addr = 2; cmd = 1; cg_arr_vals_inst.sample(); // av[1] x write
|
||||
`checkr(cg_arr_vals_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_wild_arr: wildcard array {2'b0?} matches addr 0,1; cross 2x2
|
||||
// cg_wild_arr: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
addr = 0; cmd = 0; cg_wild_arr_inst.sample();
|
||||
addr = 0; cmd = 1; cg_wild_arr_inst.sample();
|
||||
addr = 1; cmd = 0; cg_wild_arr_inst.sample();
|
||||
addr = 1; cmd = 1; cg_wild_arr_inst.sample();
|
||||
`checkr(cg_wild_arr_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_wild_solo: single wildcard bin {2'b0?} matches addr 0,1; cross 1x2
|
||||
// cg_wild_solo: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
addr = 0; cmd = 0; cg_wild_solo_inst.sample();
|
||||
addr = 0; cmd = 1; cg_wild_solo_inst.sample();
|
||||
`checkr(cg_wild_solo_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} matches addr=0 (2-state sim); cross 1x2
|
||||
// cg_arr_4state: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
addr = 0; cmd = 0; cg_arr_4state_inst.sample();
|
||||
addr = 0; cmd = 1; cg_arr_4state_inst.sample();
|
||||
`checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
|
||||
// Sample cg_overlap: overlapping range bins lo={0,1}, hi={1,2}; cross 2x2
|
||||
// cg_overlap: 2+2+4=8 bins; cover lo/hi via addr 0 and 2, plus addr=1 double-hits both
|
||||
addr = 0; cmd = 0; cg_overlap_inst.sample(); // lo x read
|
||||
addr = 0; cmd = 1; cg_overlap_inst.sample(); // lo x write
|
||||
addr = 2; cmd = 0; cg_overlap_inst.sample(); // hi x read
|
||||
addr = 2; cmd = 1; cg_overlap_inst.sample(); // hi x write
|
||||
addr = 1; cmd = 0; cg_overlap_inst.sample(); // addr=1 in both lo and hi (hit-list bound 2)
|
||||
`checkr(cg_overlap_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_wide: 64-bit coverpoint, lo={0,1}, hi={2,3}; cross 2x2
|
||||
// cg_wide: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
wide = 0; cmd = 0; cg_wide_inst.sample(); // lo x read
|
||||
wide = 0; cmd = 1; cg_wide_inst.sample(); // lo x write
|
||||
wide = 2; cmd = 0; cg_wide_inst.sample(); // hi x read
|
||||
wide = 2; cmd = 1; cg_wide_inst.sample(); // hi x write
|
||||
`checkr(cg_wide_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_openrange: lo=[$:1] matches 0,1; hi=[2:$] matches 2,3; cross 2x2
|
||||
// cg_openrange: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
addr = 0; cmd = 0; cg_openrange_inst.sample(); // lo x read
|
||||
addr = 0; cmd = 1; cg_openrange_inst.sample(); // lo x write
|
||||
addr = 2; cmd = 0; cg_openrange_inst.sample(); // hi x read
|
||||
addr = 2; cmd = 1; cg_openrange_inst.sample(); // hi x write
|
||||
`checkr(cg_openrange_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_inv: inverted range bin never matches; only cmd bins are hittable
|
||||
// cg_inv: 1+2+2=5 bins; inv and its 2 cross bins never hit -> 2/5=40%
|
||||
addr = 0; cmd = 0; cg_inv_inst.sample(); // read
|
||||
addr = 1; cmd = 1; cg_inv_inst.sample(); // write
|
||||
`checkr(cg_inv_inst.get_inst_coverage(), 40.0); // 2/5: read + write only
|
||||
|
||||
// Sample cg_noNormal: coverpoint has no Normal bins; cross product is empty
|
||||
// cg_noNormal: 0+2+0=2 bins (cmd only); both hit -> 100%
|
||||
addr = 0; cmd = 0; cg_noNormal_inst.sample(); // read
|
||||
addr = 1; cmd = 1; cg_noNormal_inst.sample(); // write
|
||||
`checkr(cg_noNormal_inst.get_inst_coverage(), 100.0); // 2/2
|
||||
|
||||
// Sample cg_trans: transition coverpoint crossed with a value coverpoint
|
||||
// cg_trans: 1+2+2=5 bins; t01_x_v6 never completes -> 4/5=80%
|
||||
// __Vprev_cp_t initializes to 0.
|
||||
state = 0; val = 5; cg_trans_inst.sample(); // prev=0,cur=0: no t01; v5
|
||||
state = 1; val = 5; cg_trans_inst.sample(); // prev=0,cur=1: t01 completes; t01_x_v5
|
||||
state = 0; val = 6; cg_trans_inst.sample(); // prev=1,cur=0: no t01; v6 (no cross)
|
||||
`checkr(cg_trans_inst.get_inst_coverage(), 80.0); // 4/5: t01_x_v6 not hit
|
||||
|
||||
// Sample cg_be: range bins over an ascending-declared coverpoint; cross 2x2
|
||||
// cg_be: 2+2+4=8 bins; endianness must not change results (mirror of cg_range)
|
||||
be_addr = 0; be_cmd = 0; cg_be_inst.sample(); // lo x read
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
be_addr = 2; be_cmd = 1; cg_be_inst.sample(); // hi x write
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
be_addr = 1; be_cmd = 1; cg_be_inst.sample(); // lo x write
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
be_addr = 3; be_cmd = 0; cg_be_inst.sample(); // hi x read
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_be_arr: array range bin over an ascending-declared coverpoint; cross 2x2
|
||||
// cg_be_arr: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
be_addr = 0; be_cmd = 0; cg_be_arr_inst.sample(); // av[0] x read
|
||||
be_addr = 0; be_cmd = 1; cg_be_arr_inst.sample(); // av[0] x write
|
||||
be_addr = 1; be_cmd = 0; cg_be_arr_inst.sample(); // av[1] x read
|
||||
be_addr = 1; be_cmd = 1; cg_be_arr_inst.sample(); // av[1] x write
|
||||
`checkr(cg_be_arr_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
# Compile the cross-coverage design with --protect-ids and confirm the coverage
|
||||
# database is obfuscated like line/toggle points: none of the "cgsecret"-marked
|
||||
# covergroup/coverpoint/cross/bin names, nor the source filename, may leak into
|
||||
# coverage.dat. (Historically covergroup coverage bypassed --protect-ids and leaked
|
||||
# these in cleartext.) The design also self-checks get_inst_coverage(), so sampling
|
||||
# correctness under hashed names is verified too.
|
||||
#
|
||||
# Everything that must stay hidden spells "secret" -- the identifiers in the .v and
|
||||
# the filename itself -- so one grep covers the whole property.
|
||||
test.compile(verilator_flags2=[
|
||||
'--coverage',
|
||||
'--protect-ids',
|
||||
'--protect-key SECRET_KEY',
|
||||
'-Wno-INSECURE',
|
||||
])
|
||||
|
||||
test.execute()
|
||||
|
||||
# Security property: no original identifier or source filename in the coverage DB.
|
||||
test.file_grep_not(test.coverage_filename, r'secret')
|
||||
|
||||
# Sanity: 'to="PS"' in the id map means something already-protected was re-protected.
|
||||
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "__idmap.xml", r'to="PS')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// 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
|
||||
|
||||
// Test cross coverage compiled with --protect-ids. --protect-ids obfuscates
|
||||
// identifiers in the generated C++ for IP protection, and the coverage database must
|
||||
// be obfuscated too -- exactly as line/toggle coverage points are. Every covergroup,
|
||||
// coverpoint, cross and bin name here carries the distinctive "cgsecret" marker so the
|
||||
// driver can assert (via file_grep_not) that none of them leak into coverage.dat. The
|
||||
// get_inst_coverage() self-checks additionally confirm sampling still works when the
|
||||
// names are hashed.
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [1:0] cgsecret_addr;
|
||||
logic cgsecret_cmd;
|
||||
|
||||
// Value-bin cross
|
||||
covergroup cgsecret_cg;
|
||||
cgsecret_cp_addr: coverpoint cgsecret_addr {bins cgsecret_a0 = {0}; bins cgsecret_a1 = {1};}
|
||||
cgsecret_cp_cmd: coverpoint cgsecret_cmd {bins cgsecret_rd = {0}; bins cgsecret_wr = {1};}
|
||||
cgsecret_ac: cross cgsecret_cp_addr, cgsecret_cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Range-bin cross (cross bin names are built at runtime from the range-bin names)
|
||||
covergroup cgsecret_cg_rng;
|
||||
cgsecret_cp_addr: coverpoint cgsecret_addr {bins cgsecret_lo = {[0 : 1]}; bins cgsecret_hi = {[2 : 3]};}
|
||||
cgsecret_cp_cmd: coverpoint cgsecret_cmd {bins cgsecret_rd = {0}; bins cgsecret_wr = {1};}
|
||||
cgsecret_rc: cross cgsecret_cp_addr, cgsecret_cp_cmd;
|
||||
endgroup
|
||||
|
||||
cgsecret_cg cg_inst = new;
|
||||
cgsecret_cg_rng cg_rng_inst = new;
|
||||
|
||||
initial begin
|
||||
// cg_inst: 2 + 2 + 4 = 8 bins; hit all four cross combinations
|
||||
cgsecret_addr = 0; cgsecret_cmd = 0; cg_inst.sample(); // a0 x rd
|
||||
cgsecret_addr = 1; cgsecret_cmd = 1; cg_inst.sample(); // a1 x wr
|
||||
cgsecret_addr = 0; cgsecret_cmd = 1; cg_inst.sample(); // a0 x wr
|
||||
cgsecret_addr = 1; cgsecret_cmd = 0; cg_inst.sample(); // a1 x rd
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// cg_rng_inst: 2 + 2 + 4 = 8 bins; hit all four cross combinations
|
||||
cgsecret_addr = 0; cgsecret_cmd = 0; cg_rng_inst.sample(); // lo x rd
|
||||
cgsecret_addr = 2; cgsecret_cmd = 1; cg_rng_inst.sample(); // hi x wr
|
||||
cgsecret_addr = 1; cgsecret_cmd = 1; cg_rng_inst.sample(); // lo x wr
|
||||
cgsecret_addr = 3; cgsecret_cmd = 0; cg_rng_inst.sample(); // hi x rd
|
||||
`checkr(cg_rng_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -7,5 +7,7 @@ cg.cp_trans2.trans2: 1
|
|||
cg.cp_trans2.trans3: 1
|
||||
cg.cp_trans3.seq_a: 1
|
||||
cg.cp_trans3.seq_b: 1
|
||||
cg_legacy.cp_mix.tr: 1
|
||||
cg_legacy.cp_mix.vals: 1
|
||||
cg_array.cp_mix.tr: 1
|
||||
cg_array.cp_mix.vals[0]: 1
|
||||
cg_array.cp_mix.vals[1]: 0
|
||||
cg_array.cp_mix.vals[2]: 0
|
||||
|
|
|
|||
|
|
@ -39,17 +39,18 @@ module t;
|
|||
}
|
||||
endgroup
|
||||
|
||||
// Non-convertible coverpoint (it has a transition bin) that also carries a value-array bin,
|
||||
// so the legacy array codegen path (which converted coverpoints bypass) is still exercised.
|
||||
covergroup cg_legacy;
|
||||
// Coverpoint mixing a transition bin and a value-array bin: both route through the
|
||||
// VlCoverpoint runtime (the value array reports indexed as vals[0]..vals[N-1], like every
|
||||
// other array bin; the transition records into its own runtime bin).
|
||||
covergroup cg_array;
|
||||
cp_mix: coverpoint state {
|
||||
bins tr = (0 => 1);
|
||||
bins vals[] = {2, [4:5]}; // discrete + range elements (both legacy array paths)
|
||||
bins vals[] = {2, [4:5]}; // discrete + range elements
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst = new;
|
||||
cg_legacy cg_legacy_inst = new;
|
||||
cg_array cg_array_inst = new;
|
||||
|
||||
initial begin
|
||||
// Drive sequence 0->1->2->3->4 which hits all bins
|
||||
|
|
@ -66,11 +67,11 @@ module t;
|
|||
cg_inst.sample(); // 3=>4: seq_b done
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// cg_legacy: exercise legacy array codegen (non-convertible coverpoint)
|
||||
state = 0; cg_legacy_inst.sample();
|
||||
state = 1; cg_legacy_inst.sample(); // 0=>1: tr
|
||||
state = 2; cg_legacy_inst.sample(); // vals
|
||||
state = 3; cg_legacy_inst.sample(); // vals
|
||||
// cg_array: exercise the mixed transition + value-array coverpoint
|
||||
state = 0; cg_array_inst.sample();
|
||||
state = 1; cg_array_inst.sample(); // 0=>1: tr
|
||||
state = 2; cg_array_inst.sample(); // vals
|
||||
state = 3; cg_array_inst.sample(); // vals
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
module t;
|
||||
%000001 logic [1:0] addr;
|
||||
-000000 point: type=toggle comment=addr[0]:0->1 hier=top.t
|
||||
-000001 point: type=toggle comment=addr[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=addr[0]:1->0 hier=top.t
|
||||
-000001 point: type=toggle comment=addr[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=addr[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=addr[1]:1->0 hier=top.t
|
||||
%000001 logic cmd;
|
||||
-000001 point: type=toggle comment=cmd:0->1 hier=top.t
|
||||
|
|
@ -27,6 +27,168 @@
|
|||
%000001 logic parity;
|
||||
-000001 point: type=toggle comment=parity:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=parity:1->0 hier=top.t
|
||||
%000001 logic [63:0] wide;
|
||||
-000000 point: type=toggle comment=wide[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[0]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[10]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[10]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[11]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[11]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[12]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[12]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[13]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[13]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[14]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[14]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[15]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[15]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[16]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[16]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[17]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[17]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[18]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[18]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[19]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[19]:1->0 hier=top.t
|
||||
-000001 point: type=toggle comment=wide[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[1]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[20]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[20]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[21]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[21]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[22]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[22]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[23]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[23]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[24]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[24]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[25]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[25]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[26]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[26]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[27]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[27]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[28]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[28]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[29]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[29]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[2]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[2]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[30]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[30]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[31]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[31]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[32]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[32]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[33]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[33]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[34]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[34]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[35]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[35]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[36]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[36]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[37]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[37]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[38]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[38]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[39]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[39]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[3]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[3]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[40]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[40]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[41]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[41]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[42]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[42]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[43]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[43]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[44]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[44]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[45]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[45]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[46]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[46]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[47]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[47]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[48]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[48]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[49]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[49]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[4]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[4]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[50]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[50]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[51]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[51]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[52]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[52]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[53]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[53]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[54]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[54]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[55]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[55]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[56]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[56]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[57]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[57]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[58]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[58]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[59]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[59]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[5]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[5]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[60]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[60]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[61]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[61]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[62]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[62]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[63]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[63]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[6]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[6]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[7]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[7]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[8]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[8]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[9]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=wide[9]:1->0 hier=top.t
|
||||
%000000 logic [3:0] state;
|
||||
-000000 point: type=toggle comment=state[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=state[0]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=state[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=state[1]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=state[2]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=state[2]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=state[3]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=state[3]:1->0 hier=top.t
|
||||
%000001 logic [3:0] val;
|
||||
-000000 point: type=toggle comment=val[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=val[0]:1->0 hier=top.t
|
||||
-000001 point: type=toggle comment=val[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=val[1]:1->0 hier=top.t
|
||||
-000001 point: type=toggle comment=val[2]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=val[2]:1->0 hier=top.t
|
||||
-000000 point: type=toggle comment=val[3]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=val[3]:1->0 hier=top.t
|
||||
|
||||
// Ascending ("opposite-endian") bit ranges: bit 0 is the MSB. Bit ordering must
|
||||
// not change coverage results: range bins operate on the sampled value, so an
|
||||
// ascending-declared coverpoint produces identical hit counts to the usual
|
||||
// descending declaration.
|
||||
/* verilator lint_off ASCRANGE */
|
||||
%000001 logic [0:1] be_addr;
|
||||
-000000 point: type=toggle comment=be_addr[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=be_addr[0]:1->0 hier=top.t
|
||||
-000001 point: type=toggle comment=be_addr[1]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=be_addr[1]:1->0 hier=top.t
|
||||
%000001 logic [0:0] be_cmd;
|
||||
-000001 point: type=toggle comment=be_cmd[0]:0->1 hier=top.t
|
||||
-000000 point: type=toggle comment=be_cmd[0]:1->0 hier=top.t
|
||||
/* verilator lint_on ASCRANGE */
|
||||
|
||||
typedef struct packed {logic m_p; logic h_mode;} cfg_t;
|
||||
%000001 cfg_t s_cfg = '0;
|
||||
|
|
@ -292,8 +454,8 @@
|
|||
// cross: [a1, write]
|
||||
endgroup
|
||||
|
||||
// Cross plus an un-crossed coverpoint: get_inst_coverage must combine the converted
|
||||
// (VlCoverpoint) coverpoint cp_solo with the legacy cross/crossed-coverpoint bins.
|
||||
// Cross plus an un-crossed coverpoint: get_inst_coverage must combine the un-crossed
|
||||
// coverpoint cp_solo with the cross and its crossed coverpoints.
|
||||
covergroup cg_mixed;
|
||||
%000002 cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_mixed.cp_addr.addr0
|
||||
|
|
@ -315,8 +477,8 @@
|
|||
// cross: [addr1, write]
|
||||
endgroup
|
||||
|
||||
// Crossed (hence non-convertible) coverpoint that also has a default bin: exercises the
|
||||
// legacy default-bin codegen path that converted coverpoints bypass.
|
||||
// Crossed coverpoint that also has a default bin: exercises default-bin handling on a
|
||||
// coverpoint that feeds a cross.
|
||||
covergroup cg_def_cross;
|
||||
%000001 cp_a: coverpoint addr iff (mode) {bins a0 = {0}; bins a1 = {1}; bins ad = default;}
|
||||
-000001 point: type=covergroup comment= hier=cg_def_cross.cp_a.a0
|
||||
|
|
@ -336,6 +498,239 @@
|
|||
// cross: [a1, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an array range bin (bins av[] = {[0:1]}): exercises the
|
||||
// coverpoint hit-list sizing for array range elements feeding a cross.
|
||||
covergroup cg_arr_range;
|
||||
%000002 cp_addr: coverpoint addr {bins av[] = {[0 : 1]};} // -> av[0]=0, av[1]=1
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_range.cp_addr.av[0]
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_range.cp_addr.av[1]
|
||||
%000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_range.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_range.cp_cmd.write
|
||||
%000001 ar: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_range.ar.av[0]_x_read
|
||||
// cross: [av[0], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_range.ar.av[0]_x_write
|
||||
// cross: [av[0], write]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_range.ar.av[1]_x_read
|
||||
// cross: [av[1], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_range.ar.av[1]_x_write
|
||||
// cross: [av[1], write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an array value bin (bins av[] = {0, 2}): array value elements.
|
||||
covergroup cg_arr_vals;
|
||||
%000002 cp_addr: coverpoint addr {bins av[] = {0, 2};} // -> av[0]=0, av[1]=2
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_vals.cp_addr.av[0]
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_vals.cp_addr.av[1]
|
||||
%000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_vals.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_vals.cp_cmd.write
|
||||
%000001 av: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_vals.av.av[0]_x_read
|
||||
// cross: [av[0], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_vals.av.av[0]_x_write
|
||||
// cross: [av[0], write]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_vals.av.av[1]_x_read
|
||||
// cross: [av[1], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_vals.av.av[1]_x_write
|
||||
// cross: [av[1], write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a wildcard array bin (wildcard bins wb[] = {2'b0?}): the '0?'
|
||||
// wildcard expands to addr values 0 and 1, each its own array element.
|
||||
covergroup cg_wild_arr;
|
||||
%000004 cp_addr: coverpoint addr {wildcard bins wb[] = {2'b0?};}
|
||||
-000004 point: type=covergroup comment= hier=cg_wild_arr.cp_addr.wb
|
||||
%000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_wild_arr.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_wild_arr.cp_cmd.write
|
||||
%000002 wa: cross cp_addr, cp_cmd;
|
||||
-000002 point: type=covergroup comment= hier=cg_wild_arr.wa.wb_x_read
|
||||
// cross: [wb, read]
|
||||
-000002 point: type=covergroup comment= hier=cg_wild_arr.wa.wb_x_write
|
||||
// cross: [wb, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a non-array wildcard bin (wildcard bins wb = {2'b0?}): single
|
||||
// wildcard bin matching addr 0 or 1, feeding a cross.
|
||||
covergroup cg_wild_solo;
|
||||
%000002 cp_addr: coverpoint addr {wildcard bins wb = {2'b0?};}
|
||||
-000002 point: type=covergroup comment= hier=cg_wild_solo.cp_addr.wb
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_wild_solo.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_wild_solo.cp_cmd.write
|
||||
%000001 ws: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_wild_solo.ws.wb_x_read
|
||||
// cross: [wb, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_wild_solo.ws.wb_x_write
|
||||
// cross: [wb, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with a four-state literal in a non-wildcard array bin
|
||||
// (bins av[] = {2'b0x}): LRM 1800-2023 19.5.4 permits 4-state values in a bin definition.
|
||||
// The hit-list sizing cannot statically analyze a 4-state value, so it falls back to the
|
||||
// safe slot count. Under Verilator's 2-state simulation the value matches addr=0.
|
||||
covergroup cg_arr_4state;
|
||||
%000002 cp_addr: coverpoint addr {bins av[] = {2'b0x};}
|
||||
-000002 point: type=covergroup comment= hier=cg_arr_4state.cp_addr.av[0]
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.cp_cmd.write
|
||||
%000001 a4: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.a4.av[0]_x_read
|
||||
// cross: [av[0], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_arr_4state.a4.av[0]_x_write
|
||||
// cross: [av[0], write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with two *overlapping* Normal range bins (addr=1 is in both lo and
|
||||
// hi): the hit-list sizing must report a bound > 1 (a single sample can fall in two bins),
|
||||
// exercising the max-overlap computation in computeHitListBound.
|
||||
covergroup cg_overlap;
|
||||
%000003 cp_addr: coverpoint addr {bins lo = {[0 : 1]}; bins hi = {[1 : 2]};} // overlap at addr=1
|
||||
-000003 point: type=covergroup comment= hier=cg_overlap.cp_addr.lo
|
||||
-000003 point: type=covergroup comment= hier=cg_overlap.cp_addr.hi
|
||||
%000003 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000003 point: type=covergroup comment= hier=cg_overlap.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_overlap.cp_cmd.write
|
||||
%000002 ov: cross cp_addr, cp_cmd;
|
||||
-000002 point: type=covergroup comment= hier=cg_overlap.ov.hi_x_read
|
||||
// cross: [hi, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_overlap.ov.hi_x_write
|
||||
// cross: [hi, write]
|
||||
-000002 point: type=covergroup comment= hier=cg_overlap.ov.lo_x_read
|
||||
// cross: [lo, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_overlap.ov.lo_x_write
|
||||
// cross: [lo, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint whose sampled expression is wider than 64 bits: exercises the
|
||||
// width>=64 max-value path in the hit-list sizing (where 1<<width would overflow).
|
||||
covergroup cg_wide;
|
||||
%000002 cp_wide: coverpoint wide {bins lo = {[0 : 1]}; bins hi = {[2 : 3]};}
|
||||
-000002 point: type=covergroup comment= hier=cg_wide.cp_wide.lo
|
||||
-000002 point: type=covergroup comment= hier=cg_wide.cp_wide.hi
|
||||
%000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_wide.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_wide.cp_cmd.write
|
||||
%000001 wd: cross cp_wide, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_wide.wd.hi_x_read
|
||||
// cross: [hi, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_wide.wd.hi_x_write
|
||||
// cross: [hi, write]
|
||||
-000001 point: type=covergroup comment= hier=cg_wide.wd.lo_x_read
|
||||
// cross: [lo, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_wide.wd.lo_x_write
|
||||
// cross: [lo, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with open-ended ('$') range bins: 'lo' is open-low ([$:1]) and 'hi'
|
||||
// is open-high ([2:$]); exercises the unbounded-bound interval handling in the hit-list
|
||||
// sizing (lo clamps to 0, hi clamps to the type max).
|
||||
covergroup cg_openrange;
|
||||
%000002 cp_addr: coverpoint addr {bins lo = {[$ : 1]}; bins hi = {[2 : $]};}
|
||||
-000002 point: type=covergroup comment= hier=cg_openrange.cp_addr.lo
|
||||
-000002 point: type=covergroup comment= hier=cg_openrange.cp_addr.hi
|
||||
%000002 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_openrange.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_openrange.cp_cmd.write
|
||||
%000001 orc: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_openrange.orc.hi_x_read
|
||||
// cross: [hi, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_openrange.orc.hi_x_write
|
||||
// cross: [hi, write]
|
||||
-000001 point: type=covergroup comment= hier=cg_openrange.orc.lo_x_read
|
||||
// cross: [lo, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_openrange.orc.lo_x_write
|
||||
// cross: [lo, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with an inverted range bin (lo bound > hi bound): the bin matches no
|
||||
// value, so the hit-list sizing rejects it (lo > hi) and falls back to the safe slot count.
|
||||
// The 'inv' bin and its cross bins are therefore never hit (coverage stays at 40%).
|
||||
covergroup cg_inv;
|
||||
%000000 cp_addr: coverpoint addr {bins inv = {[3 : 0]};} // inverted -> never matches
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.cp_addr.inv
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_inv.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_inv.cp_cmd.write
|
||||
%000000 iv: cross cp_addr, cp_cmd;
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.iv.inv_x_read
|
||||
// cross: [inv, read]
|
||||
-000000 point: type=covergroup comment= hier=cg_inv.iv.inv_x_write
|
||||
// cross: [inv, write]
|
||||
endgroup
|
||||
|
||||
// Crossed coverpoint with *no* Normal bins (only ignore_bins): the cross has an empty bin
|
||||
// product, so the hit-list sizing returns the safe bound of 1. Coverage is the cmd
|
||||
// coverpoint alone (vacuously 100% once both cmd bins are hit).
|
||||
covergroup cg_noNormal;
|
||||
%000002 cp_addr: coverpoint addr {ignore_bins ig = {[0 : 3]};} // zero Normal bins
|
||||
-000002 point: type=covergroup comment= hier=cg_noNormal.cp_addr.ig
|
||||
%000001 cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};}
|
||||
-000001 point: type=covergroup comment= hier=cg_noNormal.cp_cmd.read
|
||||
-000001 point: type=covergroup comment= hier=cg_noNormal.cp_cmd.write
|
||||
nn: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross of a *transition* coverpoint with a value coverpoint. Transition coverpoints route
|
||||
// through the VlCoverpoint runtime (their completion appends to the hit list), so a cross
|
||||
// can read them like any other coverpoint.
|
||||
covergroup cg_trans;
|
||||
%000001 cp_t: coverpoint state {bins t01 = (0 => 1);} // one Normal (transition) bin
|
||||
-000001 point: type=covergroup comment= hier=cg_trans.cp_t.t01
|
||||
%000002 cp_v: coverpoint val {bins v5 = {5}; bins v6 = {6};}
|
||||
-000002 point: type=covergroup comment= hier=cg_trans.cp_v.v5
|
||||
-000001 point: type=covergroup comment= hier=cg_trans.cp_v.v6
|
||||
%000001 tx: cross cp_t, cp_v;
|
||||
-000001 point: type=covergroup comment= hier=cg_trans.tx.t01_x_v5
|
||||
// cross: [t01, v5]
|
||||
-000000 point: type=covergroup comment= hier=cg_trans.tx.t01_x_v6
|
||||
// cross: [t01, v6]
|
||||
endgroup
|
||||
|
||||
// Range bins over an ascending-declared coverpoint, crossed with another: the
|
||||
// range interval extraction must ignore bit endianness (mirror of cg_range).
|
||||
covergroup cg_be;
|
||||
%000002 cp_addr: coverpoint be_addr {bins lo = {[0 : 1]}; bins hi = {[2 : 3]};}
|
||||
-000002 point: type=covergroup comment= hier=cg_be.cp_addr.lo
|
||||
-000002 point: type=covergroup comment= hier=cg_be.cp_addr.hi
|
||||
%000002 cp_cmd: coverpoint be_cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_be.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_be.cp_cmd.write
|
||||
%000001 x: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_be.x.hi_x_read
|
||||
// cross: [hi, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_be.x.hi_x_write
|
||||
// cross: [hi, write]
|
||||
-000001 point: type=covergroup comment= hier=cg_be.x.lo_x_read
|
||||
// cross: [lo, read]
|
||||
-000001 point: type=covergroup comment= hier=cg_be.x.lo_x_write
|
||||
// cross: [lo, write]
|
||||
endgroup
|
||||
|
||||
// Array range bin over an ascending-declared coverpoint: {[0:1]} enumerates to
|
||||
// av[0]=0, av[1]=1, exercising array-range slot enumeration under opposite
|
||||
// endian (mirror of cg_arr_range).
|
||||
covergroup cg_be_arr;
|
||||
%000002 cp_addr: coverpoint be_addr {bins av[] = {[0 : 1]};}
|
||||
-000002 point: type=covergroup comment= hier=cg_be_arr.cp_addr.av[0]
|
||||
-000002 point: type=covergroup comment= hier=cg_be_arr.cp_addr.av[1]
|
||||
%000002 cp_cmd: coverpoint be_cmd {bins read = {0}; bins write = {1};}
|
||||
-000002 point: type=covergroup comment= hier=cg_be_arr.cp_cmd.read
|
||||
-000002 point: type=covergroup comment= hier=cg_be_arr.cp_cmd.write
|
||||
%000001 ax: cross cp_addr, cp_cmd;
|
||||
-000001 point: type=covergroup comment= hier=cg_be_arr.ax.av[0]_x_read
|
||||
// cross: [av[0], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_be_arr.ax.av[0]_x_write
|
||||
// cross: [av[0], write]
|
||||
-000001 point: type=covergroup comment= hier=cg_be_arr.ax.av[1]_x_read
|
||||
// cross: [av[1], read]
|
||||
-000001 point: type=covergroup comment= hier=cg_be_arr.ax.av[1]_x_write
|
||||
// cross: [av[1], write]
|
||||
endgroup
|
||||
|
||||
%000001 cg2 cg2_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_ignore cg_ignore_inst = new;
|
||||
|
|
@ -360,6 +755,32 @@
|
|||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_def_cross cg_def_cross_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_arr_range cg_arr_range_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_arr_vals cg_arr_vals_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_wild_arr cg_wild_arr_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_wild_solo cg_wild_solo_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_arr_4state cg_arr_4state_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_overlap cg_overlap_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_wide cg_wide_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_openrange cg_openrange_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_inv cg_inv_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_noNormal cg_noNormal_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_trans cg_trans_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_be cg_be_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_be_arr cg_be_arr_inst = new;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
|
||||
%000001 initial begin
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
|
|
@ -737,6 +1158,198 @@
|
|||
%000001 addr = 2; cmd = 1; cg_def_cross_inst.sample(); // ad (default), write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
|
||||
// Sample cg_arr_range: array range bin {[0:1]} -> av[0]=0, av[1]=1; cross 2x2
|
||||
// cg_arr_range: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_arr_range_inst.sample(); // av[0] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_arr_range_inst.sample(); // av[0] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 0; cg_arr_range_inst.sample(); // av[1] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 1; cg_arr_range_inst.sample(); // av[1] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_arr_range_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_arr_vals: array value bin {0,2} -> av[0]=0, av[1]=2; cross 2x2
|
||||
// cg_arr_vals: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_arr_vals_inst.sample(); // av[0] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_arr_vals_inst.sample(); // av[0] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 0; cg_arr_vals_inst.sample(); // av[1] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 1; cg_arr_vals_inst.sample(); // av[1] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_arr_vals_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_wild_arr: wildcard array {2'b0?} matches addr 0,1; cross 2x2
|
||||
// cg_wild_arr: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_wild_arr_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_wild_arr_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 0; cg_wild_arr_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 1; cg_wild_arr_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_wild_arr_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_wild_solo: single wildcard bin {2'b0?} matches addr 0,1; cross 1x2
|
||||
// cg_wild_solo: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_wild_solo_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_wild_solo_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_wild_solo_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_arr_4state: 4-state literal bin {2'b0x} matches addr=0 (2-state sim); cross 1x2
|
||||
// cg_arr_4state: 1+2+2=5 bins; sample both cmd values -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_arr_4state_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_arr_4state_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_arr_4state_inst.get_inst_coverage(), 100.0); // 5/5
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_overlap: overlapping range bins lo={0,1}, hi={1,2}; cross 2x2
|
||||
// cg_overlap: 2+2+4=8 bins; cover lo/hi via addr 0 and 2, plus addr=1 double-hits both
|
||||
%000001 addr = 0; cmd = 0; cg_overlap_inst.sample(); // lo x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_overlap_inst.sample(); // lo x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 0; cg_overlap_inst.sample(); // hi x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 1; cg_overlap_inst.sample(); // hi x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 0; cg_overlap_inst.sample(); // addr=1 in both lo and hi (hit-list bound 2)
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_overlap_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_wide: 64-bit coverpoint, lo={0,1}, hi={2,3}; cross 2x2
|
||||
// cg_wide: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 wide = 0; cmd = 0; cg_wide_inst.sample(); // lo x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 wide = 0; cmd = 1; cg_wide_inst.sample(); // lo x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 wide = 2; cmd = 0; cg_wide_inst.sample(); // hi x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 wide = 2; cmd = 1; cg_wide_inst.sample(); // hi x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_wide_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_openrange: lo=[$:1] matches 0,1; hi=[2:$] matches 2,3; cross 2x2
|
||||
// cg_openrange: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_openrange_inst.sample(); // lo x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 0; cmd = 1; cg_openrange_inst.sample(); // lo x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 0; cg_openrange_inst.sample(); // hi x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 2; cmd = 1; cg_openrange_inst.sample(); // hi x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_openrange_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_inv: inverted range bin never matches; only cmd bins are hittable
|
||||
// cg_inv: 1+2+2=5 bins; inv and its 2 cross bins never hit -> 2/5=40%
|
||||
%000001 addr = 0; cmd = 0; cg_inv_inst.sample(); // read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 1; cg_inv_inst.sample(); // write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_inv_inst.get_inst_coverage(), 40.0); // 2/5: read + write only
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_noNormal: coverpoint has no Normal bins; cross product is empty
|
||||
// cg_noNormal: 0+2+0=2 bins (cmd only); both hit -> 100%
|
||||
%000001 addr = 0; cmd = 0; cg_noNormal_inst.sample(); // read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 addr = 1; cmd = 1; cg_noNormal_inst.sample(); // write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_noNormal_inst.get_inst_coverage(), 100.0); // 2/2
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_trans: transition coverpoint crossed with a value coverpoint
|
||||
// cg_trans: 1+2+2=5 bins; t01_x_v6 never completes -> 4/5=80%
|
||||
// __Vprev_cp_t initializes to 0.
|
||||
%000001 state = 0; val = 5; cg_trans_inst.sample(); // prev=0,cur=0: no t01; v5
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 state = 1; val = 5; cg_trans_inst.sample(); // prev=0,cur=1: t01 completes; t01_x_v5
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 state = 0; val = 6; cg_trans_inst.sample(); // prev=1,cur=0: no t01; v6 (no cross)
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_trans_inst.get_inst_coverage(), 80.0); // 4/5: t01_x_v6 not hit
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_be: range bins over an ascending-declared coverpoint; cross 2x2
|
||||
// cg_be: 2+2+4=8 bins; endianness must not change results (mirror of cg_range)
|
||||
%000001 be_addr = 0; be_cmd = 0; cg_be_inst.sample(); // lo x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_be_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
%000001 be_addr = 2; be_cmd = 1; cg_be_inst.sample(); // hi x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_be_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
%000001 be_addr = 1; be_cmd = 1; cg_be_inst.sample(); // lo x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_be_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
%000001 be_addr = 3; be_cmd = 0; cg_be_inst.sample(); // hi x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_be_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample cg_be_arr: array range bin over an ascending-declared coverpoint; cross 2x2
|
||||
// cg_be_arr: 2+2+4=8 bins; sample all combinations -> 100%
|
||||
%000001 be_addr = 0; be_cmd = 0; cg_be_arr_inst.sample(); // av[0] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 be_addr = 0; be_cmd = 1; cg_be_arr_inst.sample(); // av[0] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 be_addr = 1; be_cmd = 0; cg_be_arr_inst.sample(); // av[1] x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 be_addr = 1; be_cmd = 1; cg_be_arr_inst.sample(); // av[1] x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_be_arr_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
%000001 $write("*-* All Finished *-*\n");
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 $finish;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ test.scenarios('vlt')
|
|||
|
||||
test.top_filename = "t/t_covergroup_cross.v"
|
||||
|
||||
test.compile(verilator_flags2=['--coverage', '--Wno-COVERIGN'])
|
||||
# --Wno-ASCRANGE: cg_be / cg_be_arr in the shared top intentionally declare ascending [0:N]
|
||||
test.compile(verilator_flags2=['--coverage', '--Wno-COVERIGN', '--Wno-ASCRANGE'])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue