Add runtime support for covergroup cross-points (#7847)

This commit is contained in:
Matthew Ballance 2026-08-16 12:33:38 -07:00 committed by GitHub
parent e2f7755611
commit a3553e7bee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1824 additions and 719 deletions

View File

@ -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;
};

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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()