mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Support weight in coverage (#8472)
This commit is contained in:
@@ -305,6 +305,34 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
// Internals: VlFileLineDebug stores a SystemVerilog source code location. Used in
|
||||
// VlCoroutineHandle for debugging purposes, and to locate covergroup weight errors.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
|
||||
int m_lineno = 0; // Line number from sources
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
class VlExecutionProfilerBase;
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class VlFileLineDebug;
|
||||
|
||||
// Per-bin classification. A bin's kind is which set it lives in (structural),
|
||||
// not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
|
||||
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
|
||||
@@ -50,6 +52,9 @@ enum class VlCovBinKind : uint8_t {
|
||||
/// Bounded bin count, so random access by index is the primary usage.
|
||||
|
||||
class VlCoverpointIf VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
int32_t m_weight = 1; // option.weight; never negative
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
virtual ~VlCoverpointIf() = default;
|
||||
@@ -62,6 +67,12 @@ public:
|
||||
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;
|
||||
/// Weight of this item in its covergroup instance's coverage (option.weight,
|
||||
/// IEEE 1800-2023 19.11)
|
||||
int32_t weight() const { return m_weight; }
|
||||
/// Load option.weight, as evaluated by the covergroup constructor. A negative
|
||||
/// weight is reported as an error, and counts as zero.
|
||||
void weight(uint32_t value, VlFileLineDebug fileline);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -1155,12 +1155,75 @@ void VlCoverCrossDyn::finalizeBins() {
|
||||
//=============================================================================
|
||||
// VlCovergroupInst
|
||||
|
||||
// IEEE 1800-2023 19.11: coverage is the weighted average of the contributions; with a zero
|
||||
// denominator it is 0.0, or 100.0 when the covergroup's weight is zero
|
||||
static double _vl_cov_calculate(double weighted, double weights, int32_t weight) VL_PURE {
|
||||
if (weights == 0.0) return weight ? 0.0 : 100.0;
|
||||
return weighted / weights;
|
||||
}
|
||||
|
||||
// IEEE 1800-2023 19.7: a weight shall be non-negative. A negative constant is rejected when
|
||||
// verilating; a weight that is negative only at run time is reported as it is loaded, and
|
||||
// counts as zero, so that coverage stays within 0..100.
|
||||
static int32_t _vl_cov_load_weight(const char* optionp, IData value,
|
||||
VlFileLineDebug fileline) VL_MT_SAFE {
|
||||
const int32_t weight = static_cast<int32_t>(value);
|
||||
if (VL_LIKELY(weight >= 0)) return weight;
|
||||
const char* filep = ""; // VlFileLineDebug keeps the location only under VL_DEBUG
|
||||
int line = 0;
|
||||
#ifdef VL_DEBUG
|
||||
filep = fileline.filename();
|
||||
line = fileline.lineno();
|
||||
#else
|
||||
static_cast<void>(fileline);
|
||||
#endif
|
||||
const std::string where = filep && filep[0]
|
||||
? std::string{filep} + ":" + std::to_string(line) + ": "
|
||||
: std::string{};
|
||||
VL_PRINTF_MT("%%Error: %sCoverage option '%s' is set to negative value '%d';"
|
||||
" weights must be non-negative (IEEE 1800-2023 19.7)\n",
|
||||
where.c_str(), optionp, static_cast<int>(weight));
|
||||
VL_STOP_MT(filep, line, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VlCoverpointIf::weight(uint32_t value, VlFileLineDebug fileline) {
|
||||
m_weight = _vl_cov_load_weight("option.weight", value, fileline);
|
||||
}
|
||||
|
||||
VlCoverCrossDyn* VlCovergroupInst::addCrossDyn() {
|
||||
VlCoverCrossDyn* const cxp = new VlCoverCrossDyn{};
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp;
|
||||
}
|
||||
|
||||
void VlCovergroupInst::loadWeight() {
|
||||
// Only a new value, so that each negative value is reported once
|
||||
if (!m_weightp || *m_weightp == m_loadedWeight) return;
|
||||
m_loadedWeight = *m_weightp;
|
||||
m_weight = _vl_cov_load_weight("option.weight", m_loadedWeight, m_fileline);
|
||||
}
|
||||
|
||||
std::pair<double, double> VlCovergroupInst::coverageSums() const {
|
||||
double weighted = 0.0;
|
||||
double weights = 0.0;
|
||||
for (const auto& itemp : m_items) {
|
||||
double covered = 0.0;
|
||||
double total = 0.0;
|
||||
itemp->coverageParts(covered, total);
|
||||
if (total == 0.0) continue; // No bins: excluded from both sums
|
||||
weighted += itemp->weight() * (covered / total);
|
||||
weights += itemp->weight();
|
||||
}
|
||||
return {100.0 * weighted, weights};
|
||||
}
|
||||
|
||||
double VlCovergroupInst::coverage() {
|
||||
loadWeight();
|
||||
const std::pair<double, double> sums = coverageSums();
|
||||
return _vl_cov_calculate(sums.first, sums.second, m_weight);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupType / VlCovRegistry
|
||||
|
||||
@@ -1174,19 +1237,19 @@ VlCovergroupInst* VlCovergroupType::newInstance() {
|
||||
return instp;
|
||||
}
|
||||
|
||||
void VlCovergroupType::foldResidue(const VlCovergroupInst* instp) {
|
||||
double covered = 0.0;
|
||||
double total = 0.0;
|
||||
instp->coverageParts(covered, total);
|
||||
void VlCovergroupType::foldResidue(VlCovergroupInst* instp) {
|
||||
const std::pair<double, double> sums = instp->coverageSums();
|
||||
// Nothing coverable: excluded from both sums, so it moves neither the mean
|
||||
// nor the denominator. Never-sampled is different: it has bins, none hit,
|
||||
// and folds as 0%.
|
||||
if (total == 0.0) return;
|
||||
// TODO(P5): IEEE 1800-2023 19.5 defines covergroup coverage as the weighted
|
||||
// mean of the per-item ratios, not the ratio of the summed parts. This
|
||||
// matches what the generated get_inst_coverage() computes today, so that a
|
||||
// live instance and the same instance one delta after death never disagree.
|
||||
m_retired.sumCoverage += 100.0 * covered / total;
|
||||
if (sums.second == 0.0) return;
|
||||
// The same weighted average of the items as get_inst_coverage(), so that a live
|
||||
// instance and the same instance one delta after death never disagree. With the
|
||||
// weight last loaded: the object that lent option.weight is gone, and nothing may
|
||||
// be reported here, as this can run after ~VerilatedContext (see ~VlCovRegistry).
|
||||
const int32_t weight = instp->weight();
|
||||
m_retired.sumCoverage += weight * (sums.first / sums.second);
|
||||
m_retired.sumWeight += weight;
|
||||
++m_retired.count;
|
||||
}
|
||||
|
||||
@@ -1231,9 +1294,28 @@ bool VlCovergroupType::anyAttached() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
double VlCovergroupType::coverage(IData typeWeight, VlFileLineDebug fileline) {
|
||||
if (typeWeight != m_loadedTypeWeight) { // Only a new value, as in loadWeight()
|
||||
m_loadedTypeWeight = typeWeight;
|
||||
m_typeWeight = _vl_cov_load_weight("type_option.weight", typeWeight, fileline);
|
||||
}
|
||||
// Instances that have died still count: their contribution is the residue
|
||||
double sumCoverage = m_retired.sumCoverage;
|
||||
double sumWeight = m_retired.sumWeight;
|
||||
for (const auto& instp : m_insts) {
|
||||
if (instp->retained()) continue; // Already folded into the residue
|
||||
instp->loadWeight();
|
||||
const std::pair<double, double> sums = instp->coverageSums();
|
||||
if (sums.second == 0.0) continue; // A covergroup without coverage does not contribute
|
||||
sumCoverage += instp->weight() * (sums.first / sums.second);
|
||||
sumWeight += instp->weight();
|
||||
}
|
||||
return _vl_cov_calculate(sumCoverage, sumWeight, m_typeWeight);
|
||||
}
|
||||
|
||||
double VlCovergroupType::retiredCoverage() const {
|
||||
if (m_retired.count == 0) return -1.0;
|
||||
return m_retired.sumCoverage / static_cast<double>(m_retired.count);
|
||||
if (m_retired.count == 0 || m_retired.sumWeight == 0.0) return -1.0;
|
||||
return m_retired.sumCoverage / m_retired.sumWeight;
|
||||
}
|
||||
|
||||
// Defined here, not in verilated.cpp, so that the registry costs nothing in a model with no
|
||||
@@ -1252,13 +1334,23 @@ VlCovRegistry* VerilatedContext::covergroupRegistryp() VL_MT_SAFE {
|
||||
return static_cast<VlCovRegistry*>(m_covergroupsp.get());
|
||||
}
|
||||
|
||||
VlCovergroupInst* VlCovRegistry::newCovergroupInst(const char* typeName) {
|
||||
VlCovergroupType* VlCovRegistry::findOrCreateType(const char* typeName) {
|
||||
VlCovergroupType*& typep = m_byName[typeName];
|
||||
if (!typep) { // First instance of this type
|
||||
if (!typep) { // First use of this type
|
||||
m_types.emplace_back(new VlCovergroupType{});
|
||||
typep = m_types.back().get();
|
||||
}
|
||||
return typep->newInstance();
|
||||
return typep;
|
||||
}
|
||||
|
||||
VlCovergroupInst* VlCovRegistry::newCovergroupInst(const char* typeName) {
|
||||
return findOrCreateType(typeName)->newInstance();
|
||||
}
|
||||
|
||||
double VlCovRegistry::typeCoverage(const char* typeName, IData typeWeight,
|
||||
VlFileLineDebug fileline) {
|
||||
// Also for a type never instantiated, whose node then remembers type_option.weight
|
||||
return findOrCreateType(typeName)->coverage(typeWeight, fileline);
|
||||
}
|
||||
|
||||
// A covergroup object can outlive the registry: models must be destroyed before
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedCovContext;
|
||||
@@ -490,6 +491,12 @@ class VlCovergroupInst final {
|
||||
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
|
||||
#endif
|
||||
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
|
||||
// option.weight of the SV object that created this node, while that object
|
||||
// lives; borrowed through VlCovInstHandle::lendWeight().
|
||||
const IData* m_weightp = nullptr;
|
||||
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
|
||||
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
|
||||
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
|
||||
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
|
||||
|
||||
// Reads m_items to fold the residue; owns m_slot and m_retained.
|
||||
@@ -525,35 +532,49 @@ public:
|
||||
// here, and because it frees 'this'.
|
||||
bool attachDec() { return --m_attachCount == 0; }
|
||||
|
||||
// ---- instance weight (from VlCovInstHandle) ----
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_fileline = fileline;
|
||||
loadWeight();
|
||||
}
|
||||
// The lending object is being destroyed. Its members may already be gone, so
|
||||
// the weight is not read again; the last loaded value stays in effect.
|
||||
void unlendWeight(const IData* weightp) {
|
||||
if (m_weightp == weightp) m_weightp = nullptr;
|
||||
}
|
||||
/// Load option.weight from the lending object. SV writes the member directly
|
||||
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
|
||||
/// where a new value is seen, and checked once: a negative weight is reported as
|
||||
/// an error, and counts as zero.
|
||||
void loadWeight();
|
||||
/// Weight of this instance in its type's coverage (option.weight, IEEE
|
||||
/// 1800-2023 19.11.3), as last loaded; never negative.
|
||||
int32_t weight() const { return m_weight; }
|
||||
|
||||
// ---- introspection ----
|
||||
VlCovergroupType* typep() const { return m_typep; }
|
||||
uint32_t instId() const { return m_instId; }
|
||||
// True once retired but kept alive because the coverage database holds raw
|
||||
// pointers into this node's bin counts (VM_COVERAGE); see retire().
|
||||
bool retained() const { return m_retained; }
|
||||
// Sum of the instance's items' covered/total bin counts. Matches what the
|
||||
// generated get_inst_coverage() computes; see foldResidue().
|
||||
void coverageParts(double& covered, double& total) const {
|
||||
covered = 0.0;
|
||||
total = 0.0;
|
||||
for (const auto& itemp : m_items) {
|
||||
double c = 0.0;
|
||||
double t = 0.0;
|
||||
itemp->coverageParts(c, t);
|
||||
covered += c;
|
||||
total += t;
|
||||
}
|
||||
}
|
||||
/// IEEE 1800-2023 19.11 sums over the items whose coverage has a nonzero
|
||||
/// denominator: {the sum of each item's option.weight times its coverage
|
||||
/// (0..100), the sum of those weights}.
|
||||
std::pair<double, double> coverageSums() const;
|
||||
/// Instance coverage, as returned by get_inst_coverage(), in 0..100.
|
||||
double coverage();
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovRetiredAvg
|
||||
/// Per-type residue: what survives an instance's death. Fixed size, so it does
|
||||
/// not grow with churn. Weight is 1 everywhere until option.weight is plumbed.
|
||||
/// not grow with churn. Each instance contributes with its option.weight.
|
||||
|
||||
struct VlCovRetiredAvg final {
|
||||
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
|
||||
double sumCoverage = 0.0; // Sigma of per-instance coverage, each in 0..100
|
||||
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
|
||||
double sumWeight = 0.0; // Sigma of per-instance weight
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -569,11 +590,13 @@ class VlCovergroupType final {
|
||||
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
|
||||
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
|
||||
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
|
||||
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
|
||||
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Harvest instp's contribution into m_retired. Must run before instp is
|
||||
// unlinked: it reads the instance's items.
|
||||
void foldResidue(const VlCovergroupInst* instp);
|
||||
void foldResidue(VlCovergroupInst* instp);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -589,6 +612,12 @@ public:
|
||||
// True if any node here still has an SV handle bound to it, and so can be
|
||||
// retired again after the registry is destroyed. See ~VlCovRegistry.
|
||||
bool anyAttached() const;
|
||||
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
|
||||
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
|
||||
/// 19.11.3, type_option.merge_instances false). typeWeight is
|
||||
/// type_option.weight, which decides the result when no instance contributes;
|
||||
/// like option.weight, it is checked as it is loaded.
|
||||
double coverage(IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection ----
|
||||
// Test and debug only; generated code never calls these, and SV reaches them
|
||||
@@ -603,7 +632,8 @@ public:
|
||||
uint32_t createdInstanceCount() const { return m_createdInsts; }
|
||||
// Instances that have died and contributed to the residue.
|
||||
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
|
||||
// Mean coverage over the retired instances only, in 0..100; -1.0 if none.
|
||||
// Weighted mean coverage over the retired instances only, in 0..100; -1.0 if
|
||||
// none contributed or their weights sum to zero.
|
||||
double retiredCoverage() const;
|
||||
};
|
||||
|
||||
@@ -621,6 +651,7 @@ class VlCovRegistry final : public VerilatedVirtualBase {
|
||||
|
||||
// PRIVATE METHODS
|
||||
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
|
||||
VlCovergroupType* findOrCreateType(const char* typeName);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -633,6 +664,9 @@ public:
|
||||
// generated covergroup class name, already --protect-ids obfuscated, and is
|
||||
// the same string that keys the coverage database's hier/page.
|
||||
VlCovergroupInst* newCovergroupInst(const char* typeName);
|
||||
/// Type coverage of a covergroup type (get_coverage()); see
|
||||
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
|
||||
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection (see VlCovergroupType) ----
|
||||
// typeName is the obfuscated generated name, so a test using these under
|
||||
@@ -658,18 +692,21 @@ public:
|
||||
class VlCovInstHandle final {
|
||||
// MEMBERS
|
||||
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
|
||||
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Drop one attach count, retiring the node if that was the last handle.
|
||||
// Nothing may touch instp afterwards: retire() may have freed it.
|
||||
static void release(VlCovergroupInst* instp) {
|
||||
static void release(VlCovergroupInst* instp, const IData* weightp) {
|
||||
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
|
||||
instp->unlendWeight(weightp);
|
||||
if (instp->attachDec()) instp->typep()->retire(instp);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovInstHandle() = default;
|
||||
// The copy's owning object lends no weight; the node keeps reading the lender's.
|
||||
VlCovInstHandle(const VlCovInstHandle& o)
|
||||
: m_p{o.m_p} {
|
||||
if (VL_UNCOVERABLE(!m_p)) return; // Unbound source; see release above
|
||||
@@ -678,12 +715,18 @@ public:
|
||||
// Deleted, not implemented: nothing generates an assignment, and the
|
||||
// implicit one would copy m_p raw -- no attachInc, no release.
|
||||
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
|
||||
~VlCovInstHandle() { release(m_p); }
|
||||
~VlCovInstHandle() { release(m_p, m_weightp); }
|
||||
|
||||
// METHODS
|
||||
// Bind to a freshly created node, taking over the attach count of 1 it was
|
||||
// created with. Called once, from the generated covergroup constructor.
|
||||
void attach(VlCovergroupInst* p) { m_p = p; }
|
||||
// Let the node read the owning object's option.weight until this handle is
|
||||
// destroyed. Called once, from the generated constructor, after attach().
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_p->lendWeight(weightp, fileline);
|
||||
}
|
||||
VlCovergroupInst* p() const { return m_p; }
|
||||
};
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
// them as such currently, so named with a unique prefix
|
||||
typedef struct {
|
||||
string name;
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -266,7 +266,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -275,7 +275,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_coverpoint_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -284,7 +284,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_cross_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
bit strobe;
|
||||
@@ -294,14 +294,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
real real_interval;
|
||||
} vl_coverpoint_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
} vl_cross_type_options_t;
|
||||
|
||||
@@ -64,35 +64,6 @@
|
||||
// Placeholder for compiling with --protect-ids
|
||||
#define VL_UNKNOWN "<unknown>"
|
||||
|
||||
//=============================================================================
|
||||
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
|
||||
// debugging purposes.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
|
||||
int m_lineno = 0; // Line number from sources
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
|
||||
+4
-1
@@ -878,11 +878,12 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_ADD_SINGLE_NAMER, "addSingleNamer", false, "r+") \
|
||||
macro(COVERGROUP_ATTACH, "attach", false, "r") \
|
||||
macro(COVERGROUP_CLEAR_HIT_LIST, "clearHitList", false, "") \
|
||||
macro(COVERGROUP_COVERAGE_PARTS, "coverageParts", false, "TODO") \
|
||||
macro(COVERGROUP_COVERAGE, "coverage", false, "") \
|
||||
macro(COVERGROUP_FINALIZE_BINS, "finalizeBins", false, "") \
|
||||
macro(COVERGROUP_INCREMENT_BIN, "incrementBin", false, "r") \
|
||||
macro(COVERGROUP_INIT, "init", false, "r+") \
|
||||
macro(COVERGROUP_INST_P, "p", PURE, "") \
|
||||
macro(COVERGROUP_LEND_WEIGHT, "lendWeight", false, "rr") \
|
||||
macro(COVERGROUP_RECORD_HIT, "recordHit", false, "r") \
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
|
||||
macro(COVERGROUP_SAMPLE, "sample", false, "") \
|
||||
@@ -895,6 +896,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_SELECT_OR, "selectOr", false, "") \
|
||||
macro(COVERGROUP_SELECT_RANGE, "selectRange", false, "rr") \
|
||||
macro(COVERGROUP_SELECT_RANGE_W, "selectRangeW", false, "rr") \
|
||||
macro(COVERGROUP_TYPE_COVERAGE, "typeCoverage", false, "rrr") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED, "valueExcluded", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED_W, "valueExcludedW", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_FINALIZE, "valueFinalize", false, "") \
|
||||
@@ -903,6 +905,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_VALUE_RELEASE, "valueRelease", false, "") \
|
||||
macro(COVERGROUP_VALUE_TRANSITIONS, "valueTransitions", false, "r") \
|
||||
macro(COVERGROUP_VALUE_TYPE, "valueType", false, "rr") \
|
||||
macro(COVERGROUP_WEIGHT, "weight", false, "rr") \
|
||||
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
|
||||
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
|
||||
macro(DYN_CLEAR, "clear", false, "") \
|
||||
|
||||
@@ -1214,17 +1214,24 @@ class AstCoverOption final : public AstNode {
|
||||
// Coverage-option assignment
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
const VCoverOptionType m_optType; // Option being assigned
|
||||
const bool m_typeOption; // type_option (vs option)
|
||||
|
||||
public:
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_optType{optType} {
|
||||
, m_optType{optType}
|
||||
, m_typeOption{typeOption} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverOption;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCoverOptionType optType() const { return m_optType; }
|
||||
bool typeOption() const { return m_typeOption; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverOption* const asamep = VN_DBG_AS(samep, CoverOption);
|
||||
return m_optType.m_e == asamep->m_optType.m_e && m_typeOption == asamep->m_typeOption;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverTransItem final : public AstNode {
|
||||
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
|
||||
|
||||
@@ -1262,10 +1262,12 @@ void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstCoverOption::dump(std::ostream& str) const {
|
||||
Super::dump(str);
|
||||
str << " " << m_optType.ascii();
|
||||
if (typeOption()) str << " [TYPEOPT]";
|
||||
}
|
||||
void AstCoverOption::dumpJson(std::ostream& str) const {
|
||||
Super::dumpJson(str);
|
||||
str << ", \"optType\": \"" << m_optType.ascii() << "\"";
|
||||
dumpJsonBoolFuncIf(str, typeOption);
|
||||
}
|
||||
void AstCoverOtherDecl::dump(std::ostream& str) const {
|
||||
Super::dump(str);
|
||||
|
||||
+136
-87
@@ -217,6 +217,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
std::vector<AstCoverpoint*> m_coverpoints; // Coverpoints in current covergroup
|
||||
std::map<std::string, AstCoverpoint*> m_coverpointMap; // Name -> coverpoint for fast lookup
|
||||
std::vector<AstCoverCross*> m_coverCrosses; // Cross coverage items in current covergroup
|
||||
std::vector<AstCgOptionAssign*> m_cgOptions; // Covergroup-level weights, before lowering
|
||||
|
||||
struct EmbeddedEventTrigger final {
|
||||
FileLine* eventFl; // Clocking-event source location
|
||||
@@ -234,8 +235,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
};
|
||||
|
||||
std::set<std::string> m_crossedCpNames; // Coverpoints referenced by a cross
|
||||
std::vector<AstVar*> m_cpVars; // VlCoverpoint member, one per coverpoint
|
||||
std::vector<AstVar*> m_crossVars; // VlCoverCross member, one per cross
|
||||
std::map<std::string, AstVar*> m_cpVarMap; // Coverpoint name -> its VlCoverpoint member
|
||||
struct CrossBinValues final {
|
||||
AstCoverBin* binp; // Declaration owning this Normal bin
|
||||
@@ -264,14 +263,70 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||
|
||||
// METHODS
|
||||
// The covergroup's 'option' or static 'type_option' member (V3LinkParse creates both)
|
||||
AstVar* optionVar(bool typeOption) {
|
||||
AstVar* const varp = VN_AS(
|
||||
m_memberMap.findMember(m_covergroupp, typeOption ? "type_option" : "option"), Var);
|
||||
UASSERT_OBJ(varp, m_covergroupp, "Covergroup missing option member");
|
||||
return varp;
|
||||
}
|
||||
|
||||
// 'option.weight' or 'type_option.weight', per optionVarp
|
||||
AstStructSel* newWeightSel(FileLine* fl, AstVar* optionVarp, VAccess access) {
|
||||
const AstMemberDType* const memberp = VN_AS(
|
||||
m_memberMap.findMember(optionVarp->dtypep()->skipRefp(), "weight"), MemberDType);
|
||||
UASSERT_OBJ(memberp, optionVarp, "Coverage option structure missing 'weight'");
|
||||
AstNodeExpr* const fromp = optionVarp->lifetime().isStatic()
|
||||
? new AstVarRef{fl, optionVarp, access}
|
||||
: memberRef(fl, optionVarp, access);
|
||||
AstStructSel* const selp = new AstStructSel{fl, fromp, "weight"};
|
||||
selp->dtypep(memberp->subDTypep()->skipRefToEnump());
|
||||
selp->didWidth(true);
|
||||
return selp;
|
||||
}
|
||||
|
||||
// Store the covergroup-level weights (IEEE 1800-2023 19.7) where SystemVerilog and the
|
||||
// runtime read them. option.weight is evaluated by the constructor, as are the other
|
||||
// instance options; type_option.weight is constant, and initializes the static member.
|
||||
void lowerCovergroupOptions() {
|
||||
for (AstCgOptionAssign* const optp : m_cgOptions) {
|
||||
UASSERT_OBJ(optp->optType() == VCoverOptionType::WEIGHT, optp,
|
||||
"Unexpected covergroup option reaching V3Covergroup");
|
||||
FileLine* const fl = optp->fileline();
|
||||
AstAssign* const assignp = new AstAssign{
|
||||
fl, newWeightSel(fl, optionVar(optp->typeOption()), VAccess::WRITE),
|
||||
optp->valuep()->unlinkFrBack()};
|
||||
if (optp->typeOption()) {
|
||||
m_covergroupp->addMembersp(new AstInitialStatic{fl, assignp});
|
||||
VL_DO_DANGLING(pushDeletep(optp->unlinkFrBack()), optp);
|
||||
} else {
|
||||
optp->replaceWith(assignp);
|
||||
VL_DO_DANGLING(pushDeletep(optp), optp);
|
||||
}
|
||||
}
|
||||
m_cgOptions.clear();
|
||||
}
|
||||
|
||||
// Configure an item's option.weight, its weight in instance coverage (IEEE 1800-2023
|
||||
// 19.11). type_option.weight only weighs type coverage merged over the instances, which
|
||||
// type_option.merge_instances would select; without that, it has no effect.
|
||||
void generateItemWeight(FileLine* fl, AstVar* itemVarp, AstNode* optionsp) {
|
||||
for (AstNode* nodep = optionsp; nodep; nodep = nodep->nextp()) {
|
||||
const AstCoverOption* const optp = VN_AS(nodep, CoverOption);
|
||||
if (!(optp->optType() == VCoverOptionType::WEIGHT) || optp->typeOption()) continue;
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, itemVarp, VCMethod::COVERGROUP_WEIGHT,
|
||||
{optp->valuep()->cloneTree(false), fileLineDebug(optp->fileline())})
|
||||
->makeStmt());
|
||||
}
|
||||
}
|
||||
|
||||
void processCovergroup() {
|
||||
UINFO(4, "Processing covergroup: " << m_covergroupp->name() << " with "
|
||||
<< m_coverpoints.size() << " coverpoints and "
|
||||
<< m_coverCrosses.size() << " crosses");
|
||||
|
||||
m_crossedCpNames.clear();
|
||||
m_cpVars.clear();
|
||||
m_crossVars.clear();
|
||||
m_cpVarMap.clear();
|
||||
m_cpBins.clear();
|
||||
m_runtimePoints.clear();
|
||||
@@ -280,6 +335,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
m_droppedCrosses.clear();
|
||||
m_cgInstVarp = nullptr;
|
||||
|
||||
lowerCovergroupOptions();
|
||||
|
||||
// Scan every cross item to record the coverpoints it references (the cross dimensions)
|
||||
// and to flag any cross naming a bare variable -- a would-be implicit coverpoint, which
|
||||
// Verilator does not synthesize. An unresolvable item drops only that one cross (with a
|
||||
@@ -356,13 +413,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
// Generate coverage computation code (even for empty covergroups). Bin registration
|
||||
// with the coverage database is handled per coverpoint/cross by their runtime
|
||||
// registerBins() calls (emitted in generateCoverpoint/generateCross).
|
||||
|
||||
// TODO: Generate instance registry infrastructure for static get_coverage()
|
||||
// This requires:
|
||||
// - Static registry members (t_instances, s_mutex)
|
||||
// - registerInstance() / unregisterInstance() methods
|
||||
// - Proper C++ emission in EmitC backend
|
||||
// For now, get_coverage() returns 0.0 (placeholder)
|
||||
generateCoverageComputationCode();
|
||||
}
|
||||
|
||||
@@ -465,6 +515,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
autoBinMaxOut = -1; // -1 = not set at coverpoint level
|
||||
for (AstNode* optionp = coverpointp->optionsp(); optionp; optionp = optionp->nextp()) {
|
||||
AstCoverOption* const optp = VN_AS(optionp, CoverOption);
|
||||
// Weights may be non-constant; generateItemWeight() handles them
|
||||
if (optp->optType() == VCoverOptionType::WEIGHT) continue;
|
||||
AstConst* const constp = VN_CAST(optp->valuep(), Const);
|
||||
if (!constp) {
|
||||
optp->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant 'option."
|
||||
@@ -475,8 +527,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
if (optp->optType() == VCoverOptionType::AT_LEAST) {
|
||||
atLeastOut = constp->toSInt();
|
||||
} else {
|
||||
// V3LinkParse only converts at_least/auto_bin_max coverpoint options into
|
||||
// AstCoverOption (others are dropped there), so this is the only alternative.
|
||||
// V3LinkParse only converts at_least/auto_bin_max/weight coverpoint options
|
||||
// into AstCoverOption (others are dropped there), so this is the only
|
||||
// alternative.
|
||||
UASSERT_OBJ(optp->optType() == VCoverOptionType::AUTO_BIN_MAX, optp,
|
||||
"Unexpected coverpoint option type reaching V3Covergroup");
|
||||
autoBinMaxOut = constp->toSInt();
|
||||
@@ -750,6 +803,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
return typep;
|
||||
}
|
||||
|
||||
std::string covergroupProtectedName() const {
|
||||
return VIdProtect::protectWordsIf(m_covergroupp->name(), v3Global.opt.protectIds());
|
||||
}
|
||||
|
||||
// Emit the covergroup's instance handle member and the constructor statement that creates
|
||||
// its node in the per-context coverage registry. Runs before any coverpoint or cross is
|
||||
// generated, so their runtimes can be added to the node as they are created.
|
||||
@@ -762,30 +819,37 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
basicDType(fl, VBasicDTypeKwd::COVERGROUP_INSTHANDLE)};
|
||||
m_covergroupp->addMembersp(m_cgInstVarp);
|
||||
|
||||
// The type node is keyed by the covergroup type name -- the same string that keys this
|
||||
// covergroup's coverage-database hierarchy, so it is exactly as unique. Obfuscated the
|
||||
// same way, so --protect-ids exposes no new identifier.
|
||||
const std::string typeName
|
||||
= VIdProtect::protectWordsIf(m_covergroupp->name(), v3Global.opt.protectIds());
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, m_cgInstVarp, VCMethod::COVERGROUP_ATTACH,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->covergroupRegistryp()"
|
||||
"->newCovergroupInst("
|
||||
+ quoted(typeName) + ")")},
|
||||
+ quoted(covergroupProtectedName()) + ")")},
|
||||
/*usePtr=*/false)
|
||||
->makeStmt());
|
||||
// The node reads option.weight in place, so procedural assignments take effect
|
||||
AstCExpr* const weightAddrp = new AstCExpr{fl, "&"};
|
||||
weightAddrp->add(newWeightSel(fl, optionVar(false), VAccess::READ));
|
||||
m_constructorp->addStmtsp(itemCall(fl, m_cgInstVarp, VCMethod::COVERGROUP_LEND_WEIGHT,
|
||||
{weightAddrp, fileLineDebug(fl)}, /*usePtr=*/false)
|
||||
->makeStmt());
|
||||
}
|
||||
|
||||
// Emit 'this->__Vcp_x = this->__Vcg_inst.p()->addCoverpoint<K>();' (or addCross), which
|
||||
// creates the item runtime in the instance node and borrows a pointer to it.
|
||||
AstAssign* makeItemCreate(FileLine* fl, AstVar* itemVarp, VCMethod method) {
|
||||
// A '__Vcg_inst.p()-><method>()' call on the covergroup's instance node
|
||||
AstCMethodHard* instanceCall(FileLine* fl, VCMethod method) {
|
||||
// '__Vcg_inst.p()' -- a value handle, so '.' not '->'
|
||||
AstCMethodHard* const instp
|
||||
= new AstCMethodHard{fl, memberRef(fl, m_cgInstVarp), VCMethod::COVERGROUP_INST_P};
|
||||
instp->usePtr(false);
|
||||
instp->dtypeSetVoid(); // Opaque receiver; only ever the 'fromp' of the call below
|
||||
AstCMethodHard* const createp = new AstCMethodHard{fl, instp, method};
|
||||
createp->usePtr(true);
|
||||
AstCMethodHard* const callp = new AstCMethodHard{fl, instp, method};
|
||||
callp->usePtr(true);
|
||||
return callp;
|
||||
}
|
||||
|
||||
// Emit 'this->__Vcp_x = this->__Vcg_inst.p()->addCoverpoint<K>();' (or addCross), which
|
||||
// creates the item runtime in the instance node and borrows a pointer to it.
|
||||
AstAssign* makeItemCreate(FileLine* fl, AstVar* itemVarp, VCMethod method) {
|
||||
AstCMethodHard* const createp = instanceCall(fl, method);
|
||||
createp->dtypep(itemVarp->dtypep());
|
||||
return new AstAssign{fl, memberRef(fl, itemVarp, VAccess::WRITE), createp};
|
||||
}
|
||||
@@ -954,8 +1018,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
|
||||
// A literal C++ argument with no AST equivalent: a 'const char*' string literal (an SV
|
||||
// string AstConst emits '"..."s', a std::string temporary the runtime cannot borrow), a
|
||||
// VlCovBinKind enum token, a constant selection-word initializer list, or a '__V' temporary
|
||||
// declared by the enclosing AstCStmt.
|
||||
// VlCovBinKind enum token, a constant selection-word initializer list, a VlFileLineDebug, or
|
||||
// a '__V' temporary declared by the enclosing AstCStmt.
|
||||
static AstCExpr* ctext(FileLine* fl, const std::string& text) {
|
||||
return new AstCExpr{fl, text};
|
||||
}
|
||||
@@ -967,6 +1031,14 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
return "\"" + V3OutFormatter::quoteNameControls(text) + "\"";
|
||||
}
|
||||
|
||||
// A 'VlFileLineDebug' argument: where the runtime reports an error about fl's construct
|
||||
static AstCExpr* fileLineDebug(FileLine* fl) {
|
||||
const std::string filename
|
||||
= VIdProtect::protectIf(fl->filename(), v3Global.opt.protectIds());
|
||||
return ctext(fl, "VlFileLineDebug{" + quoted(filename) + ", "
|
||||
+ std::to_string(fl->lineno()) + "}");
|
||||
}
|
||||
|
||||
// Individual equality targets of an array bin (bins b[] = {values/ranges}), in order.
|
||||
// An open-ended bound ('$', AstUnbounded) resolves to the coverpoint domain: '[lo:$]'
|
||||
// covers [lo:maxVal] and '[$:hi]' covers [0:hi]. One target is produced per value; a
|
||||
@@ -1135,12 +1207,12 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
AstVar* const cpVarp = new AstVar{fl, VVarType::MEMBER, "__Vcp_" + coverpointp->name(),
|
||||
coverpointDType(fl, static_cast<uint32_t>(hitBound))};
|
||||
m_covergroupp->addMembersp(cpVarp);
|
||||
m_cpVars.push_back(cpVarp);
|
||||
m_cpVarMap[coverpointp->name()] = cpVarp;
|
||||
m_cpBins.emplace(cpVarp, CoverpointBins{});
|
||||
m_cpBins.at(cpVarp).exprp = exprp;
|
||||
// Create the runtime in the instance node first; everything below configures it.
|
||||
m_constructorp->addStmtsp(makeItemCreate(fl, cpVarp, VCMethod::COVERGROUP_ADD_COVERPOINT));
|
||||
generateItemWeight(fl, cpVarp, coverpointp->optionsp());
|
||||
|
||||
// A cross reads this coverpoint's hit list, so clear it at the start of the
|
||||
// coverpoint's sample() contribution (before any incrementBin appends to it).
|
||||
@@ -1580,18 +1652,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// "{ double __Vc = 0.0; double __Vt = 0.0; <item>->coverageParts(__Vc, __Vt);
|
||||
// __Vcov += __Vc; __Vtot += __Vt; }" -- one item's contribution to get_coverage().
|
||||
// The out-param temporaries make this a block, so only the call itself is a node.
|
||||
AstCStmt* makeCoveragePartsBlock(FileLine* fl, AstVar* itemVarp) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ double __Vc = 0.0; double __Vt = 0.0; ");
|
||||
cs->add(itemCall(fl, itemVarp, VCMethod::COVERGROUP_COVERAGE_PARTS,
|
||||
{ctext(fl, "__Vc"), ctext(fl, "__Vt")}));
|
||||
cs->add("; __Vcov += __Vc; __Vtot += __Vt; }");
|
||||
return cs;
|
||||
}
|
||||
|
||||
// Append a "{ VlCoverpoint* __Vcx_cps[] = {cp0, cp1, ...}; <call> }" statement. The brace
|
||||
// and the temporary array stay literal text -- a CMethodHard is one call, not a block --
|
||||
// but callp itself carries the member, method and '->'. Construction only: init() copies
|
||||
@@ -2435,10 +2495,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
= new AstVar{fl, VVarType::MEMBER, "__Vcx_" + crossp->name(),
|
||||
crossDType(fl, static_cast<uint32_t>(dims), layout, dynamic)};
|
||||
m_covergroupp->addMembersp(cxVarp);
|
||||
m_crossVars.push_back(cxVarp);
|
||||
m_constructorp->addStmtsp(makeItemCreate(fl, cxVarp,
|
||||
dynamic ? VCMethod::COVERGROUP_ADD_CROSS_DYN
|
||||
: VCMethod::COVERGROUP_ADD_CROSS));
|
||||
generateItemWeight(fl, cxVarp, crossp->optionsp());
|
||||
|
||||
// Constructor: init (after the coverpoints, which generate earlier) then registration.
|
||||
// Obfuscate the hierarchy/filename/page under --protect-ids as for coverpoints above.
|
||||
@@ -2661,56 +2721,35 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
// have added new members since the last scan, so clear before re-querying.
|
||||
m_memberMap.clear();
|
||||
|
||||
// Find get_coverage() and get_inst_coverage() methods
|
||||
AstFunc* const getCoveragep
|
||||
= VN_CAST(m_memberMap.findMember(m_covergroupp, "get_coverage"), Func);
|
||||
// get_inst_coverage(): the average of the coverpoints and crosses, weighted by their
|
||||
// option.weight (IEEE 1800-2023 19.11). The instance node holds their runtimes.
|
||||
AstFunc* const getInstCoveragep
|
||||
= VN_CAST(m_memberMap.findMember(m_covergroupp, "get_inst_coverage"), Func);
|
||||
= VN_AS(m_memberMap.findMember(m_covergroupp, "get_inst_coverage"), Func);
|
||||
FileLine* const instFl = getInstCoveragep->fileline();
|
||||
AstCMethodHard* const instCallp = instanceCall(instFl, VCMethod::COVERGROUP_COVERAGE);
|
||||
instCallp->dtypeSetDouble();
|
||||
getInstCoveragep->addStmtsp(new AstAssign{
|
||||
instFl, new AstVarRef{instFl, VN_AS(getInstCoveragep->fvarp(), Var), VAccess::WRITE},
|
||||
instCallp});
|
||||
|
||||
// Generate code for get_inst_coverage() (an empty covergroup returns 100%).
|
||||
generateCoverageMethodBody(getInstCoveragep);
|
||||
|
||||
// Generate code for get_coverage() (type-level)
|
||||
// NOTE: Full type-level coverage requires instance tracking infrastructure
|
||||
// For now, return 0.0 as a placeholder
|
||||
AstVar* const coverageReturnVarp = VN_AS(getCoveragep->fvarp(), Var);
|
||||
// TODO: Implement proper type-level coverage aggregation
|
||||
// This requires tracking all instances and averaging their coverage
|
||||
// For now, return 0.0
|
||||
// get_coverage(): the average of the covergroup's instances, weighted by their
|
||||
// option.weight (IEEE 1800-2023 19.11.3). Static, so the registry finds the instances.
|
||||
AstFunc* const getCoveragep
|
||||
= VN_AS(m_memberMap.findMember(m_covergroupp, "get_coverage"), Func);
|
||||
FileLine* const typeFl = getCoveragep->fileline();
|
||||
AstCExpr* const registryp
|
||||
= ctext(typeFl, "vlSymsp->_vm_contextp__->covergroupRegistryp()");
|
||||
registryp->dtypeSetVoid(); // Opaque receiver; only ever the 'fromp' of the call below
|
||||
AstCMethodHard* const typeCallp
|
||||
= new AstCMethodHard{typeFl, registryp, VCMethod::COVERGROUP_TYPE_COVERAGE};
|
||||
typeCallp->addPinsp(ctext(typeFl, quoted(covergroupProtectedName())));
|
||||
typeCallp->addPinsp(newWeightSel(typeFl, optionVar(true), VAccess::READ));
|
||||
typeCallp->addPinsp(fileLineDebug(m_covergroupp->fileline()));
|
||||
typeCallp->usePtr(true);
|
||||
typeCallp->dtypeSetDouble();
|
||||
getCoveragep->addStmtsp(new AstAssign{
|
||||
getCoveragep->fileline(),
|
||||
new AstVarRef{getCoveragep->fileline(), coverageReturnVarp, VAccess::WRITE},
|
||||
new AstConst{getCoveragep->fileline(), AstConst::RealDouble{}, 0.0}});
|
||||
UINFO(4, " Added placeholder get_coverage() (returns 0.0)");
|
||||
}
|
||||
|
||||
void generateCoverageMethodBody(AstFunc* funcp) {
|
||||
FileLine* const fl = funcp->fileline();
|
||||
AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var);
|
||||
|
||||
// Every coverpoint and cross holds its bins in the runtime (VlCoverpoint/VlCoverCross).
|
||||
// Sum their covered/total contributions via coverageParts (Normal bins only; ignore,
|
||||
// illegal, and default are excluded per LRM 19.5). A covergroup with no coverpoints
|
||||
// (and hence no crosses) has nothing to cover and reports 100%.
|
||||
if (m_cpVars.empty()) {
|
||||
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::RealDouble{}, 100.0}});
|
||||
return;
|
||||
}
|
||||
AstCStmt* const headp = new AstCStmt{fl};
|
||||
headp->add("double __Vcov = 0.0; double __Vtot = 0.0;");
|
||||
funcp->addStmtsp(headp);
|
||||
for (AstVar* const cpVarp : m_cpVars) {
|
||||
funcp->addStmtsp(makeCoveragePartsBlock(fl, cpVarp));
|
||||
}
|
||||
// Crosses contribute the same covered/total ratio as their per-tuple bins.
|
||||
for (AstVar* const cxVarp : m_crossVars) {
|
||||
funcp->addStmtsp(makeCoveragePartsBlock(fl, cxVarp));
|
||||
}
|
||||
AstCStmt* const retp = new AstCStmt{fl};
|
||||
retp->add(new AstVarRef{fl, returnVarp, VAccess::WRITE});
|
||||
retp->add(" = (__Vtot != 0.0) ? (100.0 * __Vcov / __Vtot) : 100.0;");
|
||||
funcp->addStmtsp(retp);
|
||||
typeFl, new AstVarRef{typeFl, VN_AS(getCoveragep->fvarp(), Var), VAccess::WRITE},
|
||||
typeCallp});
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
@@ -2954,6 +2993,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
for (AstCoverCross* const crossp : m_coverCrosses) {
|
||||
VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp);
|
||||
}
|
||||
// Options not lowered: the covergroup was not processed
|
||||
for (AstCgOptionAssign* const optp : m_cgOptions) {
|
||||
VL_DO_DANGLING(pushDeletep(optp->unlinkFrBack()), optp);
|
||||
}
|
||||
m_cgOptions.clear();
|
||||
}
|
||||
|
||||
class FormalRefVisitor final : public VNVisitor {
|
||||
@@ -3093,6 +3137,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
};
|
||||
for (AstCoverpoint* const cpp : m_coverpoints) scan(cpp);
|
||||
for (AstCoverCross* const crossp : m_coverCrosses) scan(crossp);
|
||||
for (AstCgOptionAssign* const optp : m_cgOptions) scan(optp);
|
||||
if (invalidp || !offenderp) return invalidp;
|
||||
|
||||
UASSERT_OBJ(m_embeddedVarp, m_covergroupp, "Embedded covergroup variable not found");
|
||||
@@ -3140,6 +3185,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
VL_RESTORER_CLEAR(m_coverpoints);
|
||||
VL_RESTORER_CLEAR(m_coverpointMap);
|
||||
VL_RESTORER_CLEAR(m_coverCrosses);
|
||||
VL_RESTORER_CLEAR(m_cgOptions);
|
||||
m_covergroupp = nodep;
|
||||
m_embeddedVarp = findEmbeddedCovergroupVar();
|
||||
m_sampleFuncp = nullptr;
|
||||
@@ -3255,6 +3301,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// V3Width leaves only the covergroup-level weights, for lowerCovergroupOptions()
|
||||
void visit(AstCgOptionAssign* nodep) override { m_cgOptions.push_back(nodep); }
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
|
||||
+16
-1
@@ -328,6 +328,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
// An option or type_option assignment, from a covergroup, coverpoint, or cross
|
||||
void emitCoverageOption(AstNode* nodep, bool typeOption, VCoverOptionType optType,
|
||||
AstNodeExpr* valuep) {
|
||||
putfs(nodep,
|
||||
std::string{typeOption ? "type_option." : "option."} + optType.ascii() + " = ");
|
||||
iterateConst(valuep);
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCgOptionAssign* nodep) override {
|
||||
emitCoverageOption(nodep, nodep->typeOption(), nodep->optType(), nodep->valuep());
|
||||
}
|
||||
void visit(AstCoverOption* nodep) override {
|
||||
emitCoverageOption(nodep, nodep->typeOption(), nodep->optType(), nodep->valuep());
|
||||
}
|
||||
void visit(AstCoverBin* nodep) override {
|
||||
switch (nodep->binsType()) {
|
||||
case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break;
|
||||
@@ -403,8 +417,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
||||
iterateConst(nodep->iffp());
|
||||
puts(")");
|
||||
}
|
||||
if (nodep->binsp()) {
|
||||
if (nodep->binsp() || nodep->optionsp()) {
|
||||
puts(" {\n");
|
||||
iterateAndNextConstNull(nodep->optionsp());
|
||||
iterateAndNextConstNull(nodep->binsp());
|
||||
puts("}\n");
|
||||
} else {
|
||||
|
||||
+12
-6
@@ -1213,12 +1213,14 @@ class LinkParseVisitor final : public VNVisitor {
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
{
|
||||
// IEEE 1800-2023 19.10: type_option is a static member, shared by all instances
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
varp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
@@ -1421,8 +1423,10 @@ class LinkParseVisitor final : public VNVisitor {
|
||||
if (dropDeprecatedCoverageOption(optp)) continue;
|
||||
optp->unlinkFrBack();
|
||||
if (optp->optType() == VCoverOptionType::AT_LEAST
|
||||
|| optp->optType() == VCoverOptionType::AUTO_BIN_MAX) {
|
||||
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optType(),
|
||||
|| optp->optType() == VCoverOptionType::AUTO_BIN_MAX
|
||||
|| optp->optType() == VCoverOptionType::WEIGHT) {
|
||||
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->typeOption(),
|
||||
optp->optType(),
|
||||
optp->valuep()->cloneTree(false)});
|
||||
} else {
|
||||
optp->v3warn(COVERIGN,
|
||||
@@ -1485,12 +1489,14 @@ class LinkParseVisitor final : public VNVisitor {
|
||||
if (dropDeprecatedCoverageOption(optp)) continue;
|
||||
itemp->unlinkFrBack();
|
||||
const VCoverOptionType optType = optp->optType();
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage cross option: " + optp->prettyNameQ());
|
||||
if (!(optType == VCoverOptionType::WEIGHT)) {
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage cross option: " + optp->prettyNameQ());
|
||||
}
|
||||
// Always preserve the option node so V3Coverage can track its source line
|
||||
// for coverage annotation, even when the option itself is unsupported.
|
||||
nodep->addOptionsp(
|
||||
new AstCoverOption{optp->fileline(), optType, optp->valuep()->cloneTree(false)});
|
||||
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->typeOption(), optType,
|
||||
optp->valuep()->cloneTree(false)});
|
||||
VL_DO_DANGLING(optp->deleteTree(), optp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
||||
+48
-1
@@ -2075,6 +2075,32 @@ class WidthVisitor final : public VNVisitor {
|
||||
assertAtExpr(nodep);
|
||||
if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
|
||||
}
|
||||
// Width a 'weight' coverage option as the signed 'int' member it sets (IEEE 1800-2023
|
||||
// 19.10); a weight is a non-negative integral value, constant for type_option (19.7)
|
||||
template <typename T_Option>
|
||||
void widthCoverWeight(T_Option* nodep) {
|
||||
iterateCheckSigned32(nodep, "weight", nodep->valuep(), BOTH);
|
||||
V3Const::constifyEdit(nodep->valuep());
|
||||
AstNodeExpr* const valuep = nodep->valuep();
|
||||
const std::string optName
|
||||
= std::string{nodep->typeOption() ? "type_option" : "option"} + ".weight";
|
||||
if (const AstConst* const constp = VN_CAST(valuep, Const)) {
|
||||
if (!constp->num().isNegative()) return;
|
||||
valuep->v3error("Coverage option '" << optName << "' is set to negative value '"
|
||||
<< constp->toSInt()
|
||||
<< "'; weights must be non-negative"
|
||||
" (IEEE 1800-2023 19.7)");
|
||||
} else if (nodep->typeOption()) {
|
||||
valuep->v3error("Coverage option '" << optName
|
||||
<< "' requires a constant expression"
|
||||
" (IEEE 1800-2023 19.7.1)");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// Continue with the default weight
|
||||
valuep->replaceWith(new AstConst{valuep->fileline(), AstConst::Signed32{}, 1});
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
}
|
||||
void visit(AstCgOptionAssign* nodep) override {
|
||||
// Recursive function widthing can reach a covergroup constructor without first visiting
|
||||
// its class, so find the owning covergroup structurally instead of using visit context.
|
||||
@@ -2082,6 +2108,12 @@ class WidthVisitor final : public VNVisitor {
|
||||
UASSERT_OBJ(cgClassp && cgClassp->isCovergroup(), nodep,
|
||||
"Covergroup option is not under a covergroup class");
|
||||
|
||||
// V3Covergroup stores the weight into the covergroup's option or type_option
|
||||
if (nodep->optType() == VCoverOptionType::WEIGHT) {
|
||||
widthCoverWeight(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract covergroup option values and store in AstClass before deleting.
|
||||
if (nodep->optType() == VCoverOptionType::AUTO_BIN_MAX) {
|
||||
// By V3Width time, V3Param has already folded any parameter references.
|
||||
@@ -2094,12 +2126,27 @@ class WidthVisitor final : public VNVisitor {
|
||||
nodep->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant "
|
||||
"'option.auto_bin_max'; using default value");
|
||||
}
|
||||
} else if (nodep->optType() == VCoverOptionType::MERGE_INSTANCES) {
|
||||
// get_coverage() always averages the instances (IEEE 1800-2023 19.11.3)
|
||||
const AstConst* const constp = VN_CAST(nodep->valuep(), Const);
|
||||
if (!constp || !constp->num().isEqZero()) {
|
||||
nodep->v3warn(COVERIGN, "Ignoring unsupported: 'type_option.merge_instances';"
|
||||
" type coverage is the weighted average of the"
|
||||
" instances");
|
||||
}
|
||||
}
|
||||
// Add more options here as needed (weight, goal, at_least, per_instance, comment)
|
||||
// Add more options here as needed (goal, at_least, per_instance, comment)
|
||||
|
||||
// Delete the assignment node (we've extracted the value)
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstCoverOption* nodep) override {
|
||||
if (nodep->optType() == VCoverOptionType::WEIGHT) {
|
||||
widthCoverWeight(nodep);
|
||||
return;
|
||||
}
|
||||
userIterateChildren(nodep, nullptr);
|
||||
}
|
||||
void visit(AstCoverCross* nodep) override {
|
||||
userIterateAndNext(nodep->itemsp(), nullptr);
|
||||
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
|
||||
|
||||
@@ -138,7 +138,7 @@ module t;
|
||||
cg7_inst.sample(); // [-4:-3]
|
||||
sdata3 = 3;
|
||||
cg7_inst.sample(); // [0:3]
|
||||
`checkr(cg7_inst.get_inst_coverage(), 100.0 * 2.0 / 3.0);
|
||||
`checkr(cg7_inst.get_inst_coverage(), 100.0 * (2.0 / 3.0));
|
||||
sdata3 = 0;
|
||||
cg7_inst.sample(); // [0:3]
|
||||
sdata3 = -1;
|
||||
|
||||
@@ -217,7 +217,8 @@ module t;
|
||||
cg_namers_inst.sample();
|
||||
cg_empty_namers_inst.sample();
|
||||
`checkr(cg_namers_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg_empty_namers_inst.get_inst_coverage(), 100.0);
|
||||
// Only empty bins: nothing contributes, so a nonzero weight gives 0 (IEEE 1800-2023 19.11)
|
||||
`checkr(cg_empty_namers_inst.get_inst_coverage(), 0.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
||||
@@ -81,8 +81,13 @@ module t (
|
||||
empty_cov.sample(1'(cyc / 2), 1'(cyc));
|
||||
if (cyc < 6) trans_cov.sample(7'(cyc % 3), 1'(cyc / 3));
|
||||
if (cyc == 0) begin
|
||||
`checkr(cov.get_inst_coverage(), 20.0);
|
||||
`checkr(auto_cov.get_inst_coverage(), 60.0);
|
||||
// IEEE 1800-2023 19.11: the mean of the items' coverage. cp_a 1/4, cp_b 2/3,
|
||||
// all_products 1/1, named 1/10, other_axis 2/9, array_bins 2/7, overlapping 2/8,
|
||||
// guarded 0/11, and empty_selection 2/12
|
||||
`checkr(cov.get_inst_coverage(),
|
||||
100.0 * (1.0 / 4 + 2.0 / 3 + 1.0 + 1.0 / 10 + 2.0 / 9 + 2.0 / 7 + 2.0 / 8 + 0.0
|
||||
+ 2.0 / 12) / 9);
|
||||
`checkr(auto_cov.get_inst_coverage(), (50.0 + 50.0 + 100.0) / 3);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
|
||||
@@ -278,34 +278,35 @@ module t;
|
||||
|
||||
initial begin
|
||||
// Sample 2-way: hit all 4 combinations
|
||||
// cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count)
|
||||
// cg2: coverage is the mean of cp_addr (2 bins), cp_cmd (2 bins), and the cross (4 bins)
|
||||
// (IEEE 1800-2023 19.11)
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
mode = 0;
|
||||
parity = 0;
|
||||
cg2_inst.sample(); // addr0 x read
|
||||
`checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
`checkr(cg2_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3); // addr0, read, one cross
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
mode = 0;
|
||||
parity = 0;
|
||||
cg2_inst.sample(); // addr1 x write
|
||||
`checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
`checkr(cg2_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3); // 2 cross bins
|
||||
addr = 0;
|
||||
cmd = 1;
|
||||
mode = 0;
|
||||
parity = 0;
|
||||
cg2_inst.sample(); // addr0 x write
|
||||
`checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit
|
||||
`checkr(cg2_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3); // 3 cross bins
|
||||
addr = 1;
|
||||
cmd = 0;
|
||||
mode = 0;
|
||||
parity = 0;
|
||||
cg2_inst.sample(); // addr1 x read
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0); // 8/8: all 4 cross bins hit
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0); // all 4 cross bins hit
|
||||
|
||||
// Sample 3-way: hit 4 of 12 combinations
|
||||
// cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr)
|
||||
// cg3: 4 of 12 cross bins hit (not clean; no intermediate checkr)
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
mode = 0;
|
||||
@@ -324,7 +325,7 @@ module t;
|
||||
cg3_inst.sample(); // addr0 x write x debug
|
||||
|
||||
// Sample 4-way: hit 4 of 16 combinations
|
||||
// cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50%
|
||||
// cg4: four 2-bin coverpoints and a 16-bin cross; 4 cross bins hit
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
mode = 0;
|
||||
@@ -335,7 +336,7 @@ module t;
|
||||
mode = 0;
|
||||
parity = 1;
|
||||
cg4_inst.sample();
|
||||
`checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins
|
||||
`checkr(cg4_inst.get_inst_coverage(), 72.5); // cp_mode 1/2, cross 2/16, others full
|
||||
addr = 0;
|
||||
cmd = 1;
|
||||
mode = 1;
|
||||
@@ -346,112 +347,111 @@ module t;
|
||||
mode = 1;
|
||||
parity = 1;
|
||||
cg4_inst.sample();
|
||||
`checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins
|
||||
`checkr(cg4_inst.get_inst_coverage(), 85.0); // all cp bins, cross 4/16
|
||||
|
||||
// Sample cg5 (cross with option.weight=2; weight is ignored in flat bin count)
|
||||
// cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// Sample cg5 (cross with option.weight=2): the cross counts twice in the mean
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
`checkr(cg5_inst.get_inst_coverage(), (50.0 + 50.0 + 2 * 25.0) / 4); // addr0, read, 1/4
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
`checkr(cg5_inst.get_inst_coverage(), (100.0 + 100.0 + 2 * 50.0) / 4); // cross 2/4
|
||||
|
||||
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
|
||||
// cg_ignore: 2+2+4=8 bins total
|
||||
// cg_ignore: 2-bin coverpoints and a 4-bin cross
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_ignore_inst.sample(); // a0 x read
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_ignore_inst.sample(); // a1 x write
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
addr = 0;
|
||||
cmd = 1;
|
||||
cg_ignore_inst.sample(); // a0 x write
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
addr = 1;
|
||||
cmd = 0;
|
||||
cg_ignore_inst.sample(); // a1 x read
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0);
|
||||
addr = 3;
|
||||
cmd = 0;
|
||||
cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins)
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // still 100%
|
||||
|
||||
// Sample range-bin cross
|
||||
// cg_range: 2+2+4=8 bins
|
||||
// cg_range: 2-bin coverpoints and a 4-bin cross
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_range_inst.sample(); // lo_range x read
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
`checkr(cg_range_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
addr = 2;
|
||||
cmd = 1;
|
||||
cg_range_inst.sample(); // hi_range x write
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
`checkr(cg_range_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_range_inst.sample(); // lo_range x write
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
`checkr(cg_range_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
addr = 3;
|
||||
cmd = 0;
|
||||
cg_range_inst.sample(); // hi_range x read
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins)
|
||||
// cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75%
|
||||
// cg_at_least: 2 of 4 cross bins hit (count=1, at_least effectively 1)
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_at_least_inst.sample(); // addr0 x read
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_at_least_inst.sample(); // addr1 x write
|
||||
`checkr(cg_at_least_inst.get_inst_coverage(), 75.0);
|
||||
`checkr(cg_at_least_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
|
||||
// Sample cg_goal (option.goal in cross body; does not affect hit counting)
|
||||
// cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_goal: 2 of 4 cross bins hit
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_goal_inst.sample(); // addr0 x read
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_goal_inst.sample(); // addr1 x write
|
||||
`checkr(cg_goal_inst.get_inst_coverage(), 75.0);
|
||||
`checkr(cg_goal_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
|
||||
// Sample cg_unsup_cross_opt
|
||||
// cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_unsup_cross_opt: 2 of 4 cross bins hit; the dropped cross contributes nothing
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_unsup_cross_opt_inst.sample(); // addr0 x read
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_unsup_cross_opt_inst.sample(); // addr1 x write
|
||||
`checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), 75.0);
|
||||
`checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
|
||||
// Sample cg_unnamed_cross
|
||||
// cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_unnamed_cross: 2 of 4 cross bins hit
|
||||
addr = 0;
|
||||
cmd = 0;
|
||||
cg_unnamed_cross_inst.sample(); // a0 x read
|
||||
addr = 1;
|
||||
cmd = 1;
|
||||
cg_unnamed_cross_inst.sample(); // a1 x write
|
||||
`checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0);
|
||||
`checkr(cg_unnamed_cross_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
|
||||
// Sample cg_mixed: 10 bins total (cp_addr 2 + cp_cmd 2 + cp_solo 2 + cross ab 4)
|
||||
// Sample cg_mixed: cp_addr, cp_cmd, and cp_solo have 2 bins each, and cross ab 4
|
||||
addr = 0; cmd = 0; mode = 0;
|
||||
cg_mixed_inst.sample(); // addr0, read, solo normal, ab(addr0_x_read)
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 40.0); // 4/10
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), (50.0 + 50.0 + 50.0 + 25.0) / 4);
|
||||
addr = 0; cmd = 1; mode = 1;
|
||||
cg_mixed_inst.sample(); // addr0, write, solo debug, ab(addr0_x_write)
|
||||
addr = 1; cmd = 0; mode = 0;
|
||||
cg_mixed_inst.sample(); // addr1, read, ab(addr1_x_read)
|
||||
addr = 1; cmd = 1; mode = 1;
|
||||
cg_mixed_inst.sample(); // addr1, write, ab(addr1_x_write)
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 100.0); // 10/10
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg_def_cross (default bin in a crossed coverpoint, gated by iff)
|
||||
mode = 1;
|
||||
@@ -532,23 +532,23 @@ module t;
|
||||
`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%
|
||||
// cg_trans: t01_x_v6 never completes, so only the cross is partial (1/2)
|
||||
// __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
|
||||
`checkr(cg_trans_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
|
||||
// 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)
|
||||
// cg_be: 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
|
||||
`checkr(cg_be_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
be_addr = 2; be_cmd = 1; cg_be_inst.sample(); // hi x write
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
`checkr(cg_be_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
be_addr = 1; be_cmd = 1; cg_be_inst.sample(); // lo x write
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
`checkr(cg_be_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
be_addr = 3; be_cmd = 0; cg_be_inst.sample(); // hi x read
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
`checkr(cg_be_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// 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%
|
||||
|
||||
@@ -40,10 +40,11 @@ module t;
|
||||
for (int i = 0; i < 4; ++i) begin
|
||||
group_cov.sample(1'(i / 2), 1'(i));
|
||||
cross_cov.sample(1'(i / 2), 1'(i));
|
||||
// IEEE 1800-2023 19.11: the mean of cp_a, cp_b, and the 4-bin cross
|
||||
case (i)
|
||||
0: check_coverage(37.5);
|
||||
1: check_coverage(62.5);
|
||||
2: check_coverage(87.5);
|
||||
0: check_coverage((50.0 + 50.0 + 25.0) / 3);
|
||||
1: check_coverage((50.0 + 100.0 + 50.0) / 3);
|
||||
2: check_coverage((100.0 + 100.0 + 75.0) / 3);
|
||||
3: check_coverage(100.0);
|
||||
default: `stop;
|
||||
endcase
|
||||
|
||||
@@ -175,13 +175,15 @@ module t (
|
||||
trans_cov.sample(1'(cyc), 1'(cyc / 2));
|
||||
three_cov.sample(1'(cyc / 4), 1'(cyc / 2), 1'(cyc));
|
||||
zero_cov.sample(1'(cyc / 2), 1'(cyc));
|
||||
// IEEE 1800-2023 19.11: the mean of cp_a (4 bins), cp_b (2 bins), and the crosses.
|
||||
// Each cg_order cross keeps only 'combined'; cg_auto's cross keeps 4 automatic bins.
|
||||
if (cyc == 1) begin
|
||||
`checkr(order_cov.get_inst_coverage(), 37.5);
|
||||
`checkr(auto_cov.get_inst_coverage(), 30.0);
|
||||
`checkr(order_cov.get_inst_coverage(), (25.0 + 100.0 + 0.0 + 0.0) / 4);
|
||||
`checkr(auto_cov.get_inst_coverage(), (25.0 + 100.0 + 0.0) / 3);
|
||||
end
|
||||
if (cyc == 5) begin
|
||||
`checkr(order_cov.get_inst_coverage(), 87.5);
|
||||
`checkr(auto_cov.get_inst_coverage(), 90.0);
|
||||
`checkr(order_cov.get_inst_coverage(), (75.0 + 100.0 + 100.0 + 100.0) / 4);
|
||||
`checkr(auto_cov.get_inst_coverage(), (75.0 + 100.0 + 100.0) / 3);
|
||||
end
|
||||
end
|
||||
if (cyc < 65) begin
|
||||
|
||||
@@ -134,7 +134,9 @@ module t;
|
||||
data = 2;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0); // cg2 has 1 bin (default) -> 100% after first sample
|
||||
// A default bin never counts toward coverage (IEEE 1800-2023 19.5), so nothing in cg2
|
||||
// contributes, and its nonzero weight gives 0 (IEEE 1800-2023 19.11)
|
||||
`checkr(cg2_inst.get_inst_coverage(), 0.0);
|
||||
|
||||
// Hit high bin
|
||||
data = 14;
|
||||
@@ -152,7 +154,7 @@ module t;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg2_inst.get_inst_coverage(), 0.0);
|
||||
|
||||
// Sample cg3: verify ignore/illegal bins do not contribute to coverage
|
||||
data = 2;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Empty covergroup coverage: 100.000000%
|
||||
*-* All Finished *-*
|
||||
@@ -10,6 +10,6 @@ test.scenarios('vlt_all')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test: Empty covergroup (no coverpoints)
|
||||
// Expected: Should compile, coverage should be 100% (nothing to cover)
|
||||
// Expected: Should compile; with nothing to cover, a covergroup of nonzero weight reports 0%
|
||||
// and one of zero weight 100% (IEEE 1800-2023 19.11)
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t (
|
||||
input clk
|
||||
@@ -17,7 +23,13 @@ module t (
|
||||
// Intentionally empty
|
||||
endgroup
|
||||
|
||||
// Empty covergroup of zero weight
|
||||
covergroup cg_empty_w0;
|
||||
option.weight = 0;
|
||||
endgroup
|
||||
|
||||
cg_empty cg_inst = new;
|
||||
cg_empty_w0 cg_w0_inst = new;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
@@ -26,11 +38,14 @@ module t (
|
||||
value <= value + 1;
|
||||
|
||||
cg_inst.sample();
|
||||
cg_w0_inst.sample();
|
||||
|
||||
if (cyc == 5) begin
|
||||
real cov;
|
||||
cov = cg_inst.get_inst_coverage();
|
||||
$display("Empty covergroup coverage: %f%%", cov);
|
||||
`checkr(cov, 0.0);
|
||||
cov = cg_w0_inst.get_inst_coverage();
|
||||
`checkr(cov, 100.0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Test that illegal_bins are excluded from coverage (like ignore_bins).
|
||||
// Also tests coverpoints where all bins are ignore/illegal - get_coverage returns 100.0.
|
||||
// Also tests coverpoints where all bins are ignore/illegal - nothing contributes to coverage.
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
@@ -36,7 +36,7 @@ module t;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg3: all bins are ignore_bins or illegal_bins - get_coverage returns 100.0
|
||||
// cg3: all bins are ignore_bins or illegal_bins - nothing contributes to coverage
|
||||
covergroup cg3;
|
||||
cp: coverpoint data {ignore_bins ign = {0, 1}; illegal_bins ill = {2, 3};}
|
||||
endgroup
|
||||
@@ -60,13 +60,14 @@ module t;
|
||||
cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg3 - values that only hit ignore_bins, never illegal_bins
|
||||
// Sample cg3 - values that only hit ignore_bins, never illegal_bins. With no coverage
|
||||
// to contribute, the covergroup's nonzero weight gives 0 (IEEE 1800-2023 19.11)
|
||||
data = 0;
|
||||
cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg3_inst.get_inst_coverage(), 0.0);
|
||||
data = 1;
|
||||
cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg3_inst.get_inst_coverage(), 0.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
||||
@@ -35,6 +35,7 @@ module t;
|
||||
bit [3:0] value;
|
||||
|
||||
covergroup cov_cplx @(posedge a or posedge mid.lvl.ev);
|
||||
option.weight = 2; // Dropped along with the covergroup
|
||||
coverpoint value {bins lo = {[0 : 7]}; bins hi = {[8 : 15]};}
|
||||
endgroup
|
||||
|
||||
|
||||
@@ -53,13 +53,15 @@ module t;
|
||||
|
||||
// inst_none is never sampled.
|
||||
|
||||
// Today: raw covered/total over all bins of all items.
|
||||
// inst_full 6/6 -> 100.0
|
||||
// inst_part 2/6 -> 33.333 (weighted average over items would be 37.5)
|
||||
// inst_none 0/6 -> 0.0
|
||||
// Instance coverage is the average of the items (IEEE 1800-2023 19.11); type coverage
|
||||
// is the average of the instances (IEEE 1800-2023 19.11.3), all at the default weight 1.
|
||||
// inst_full cp_a 4/4, cp_b 2/2 -> 100.0
|
||||
// inst_part cp_a 1/4, cp_b 1/2 -> 37.5 (the raw covered/total sum would be 33.333)
|
||||
// inst_none cp_a 0/4, cp_b 0/2 -> 0.0
|
||||
`checkr(inst_full.get_inst_coverage(), 100.0);
|
||||
`checkr(inst_part.get_inst_coverage(), 33.333);
|
||||
`checkr(inst_part.get_inst_coverage(), 37.5);
|
||||
`checkr(inst_none.get_inst_coverage(), 0.0);
|
||||
`checkr(cg_mi::get_coverage(), (100.0 + 37.5 + 0.0) / 3);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
||||
@@ -114,9 +114,6 @@
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:98:18: Ignoring unsupported coverage cross option: 'comment'
|
||||
98 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; }
|
||||
| ^~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:98:44: Ignoring unsupported coverage cross option: 'weight'
|
||||
98 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; }
|
||||
| ^~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:98:64: Ignoring unsupported coverage cross option: 'per_instance'
|
||||
98 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; }
|
||||
| ^~~~~~
|
||||
@@ -150,6 +147,10 @@
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:176:7: Unsupported: explicit coverage cross bins
|
||||
176 | illegal_bins lib_cross = binsof(a) with (a);
|
||||
| ^~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:46:5: Ignoring unsupported: 'type_option.merge_instances'; type coverage is the weighted average of the instances
|
||||
: ... note: In instance 't'
|
||||
46 | type_option.merge_instances = 1;
|
||||
| ^~~~~~~~~~~
|
||||
%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:76:5: Logical operator COVERPOINT 'a' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits.
|
||||
: ... note: In instance 't'
|
||||
76 | coverpoint a iff (b);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
__vlAnonCG_cg_emb.cpv.auto_0: 1
|
||||
__vlAnonCG_cg_emb.cpv.auto_1: 0
|
||||
__vlAnonCG_cg_emb.cpv.auto_2: 0
|
||||
__vlAnonCG_cg_emb.cpv.auto_3: 0
|
||||
__vlAnonCG_cg_emb.cpz.zero: 1
|
||||
cg_all_zero.cpa.auto_0: 1
|
||||
cg_all_zero.cpa.auto_1: 1
|
||||
cg_all_zero.cpa.auto_2: 0
|
||||
cg_all_zero.cpa.auto_3: 0
|
||||
cg_all_zero.cpb.auto_0: 2
|
||||
cg_all_zero.cpb.auto_1: 0
|
||||
cg_all_zero.cpb.auto_2: 0
|
||||
cg_all_zero.cpb.auto_3: 0
|
||||
cg_all_zero.cpb.auto_4: 0
|
||||
cg_all_zero.cpb.auto_5: 0
|
||||
cg_all_zero.cpb.auto_6: 0
|
||||
cg_all_zero.cpb.auto_7: 0
|
||||
cg_cross.cpa.a0: 1
|
||||
cg_cross.cpa.a1: 1
|
||||
cg_cross.cpb.b0: 2
|
||||
cg_cross.cpb.b1: 0
|
||||
cg_cross.x.a0_x_b0 [cross]: 1
|
||||
cg_cross.x.a0_x_b1 [cross]: 0
|
||||
cg_cross.x.a1_x_b0 [cross]: 1
|
||||
cg_cross.x.a1_x_b1 [cross]: 0
|
||||
cg_group.cpa.auto_0: 1
|
||||
cg_group.cpa.auto_1: 1
|
||||
cg_group.cpa.auto_2: 0
|
||||
cg_group.cpa.auto_3: 0
|
||||
cg_group.cpb.auto_0: 2
|
||||
cg_group.cpb.auto_1: 0
|
||||
cg_group.cpb.auto_2: 0
|
||||
cg_group.cpb.auto_3: 0
|
||||
cg_group.cpb.auto_4: 0
|
||||
cg_group.cpb.auto_5: 0
|
||||
cg_group.cpb.auto_6: 0
|
||||
cg_group.cpb.auto_7: 0
|
||||
cg_item.cpa.auto_0: 1
|
||||
cg_item.cpa.auto_1: 1
|
||||
cg_item.cpa.auto_2: 0
|
||||
cg_item.cpa.auto_3: 0
|
||||
cg_item.cpb.auto_0: 2
|
||||
cg_item.cpb.auto_1: 0
|
||||
cg_item.cpb.auto_2: 0
|
||||
cg_item.cpb.auto_3: 0
|
||||
cg_item.cpb.auto_4: 0
|
||||
cg_item.cpb.auto_5: 0
|
||||
cg_item.cpb.auto_6: 0
|
||||
cg_item.cpb.auto_7: 0
|
||||
cg_item_zero.cpa.auto_0: 1
|
||||
cg_item_zero.cpa.auto_1: 1
|
||||
cg_item_zero.cpa.auto_2: 0
|
||||
cg_item_zero.cpa.auto_3: 0
|
||||
cg_item_zero.cpb.auto_0: 2
|
||||
cg_item_zero.cpb.auto_1: 0
|
||||
cg_item_zero.cpb.auto_2: 0
|
||||
cg_item_zero.cpb.auto_3: 0
|
||||
cg_item_zero.cpb.auto_4: 0
|
||||
cg_item_zero.cpb.auto_5: 0
|
||||
cg_item_zero.cpb.auto_6: 0
|
||||
cg_item_zero.cpb.auto_7: 0
|
||||
cg_type.cpa.auto_0: 1
|
||||
cg_type.cpa.auto_1: 1
|
||||
cg_type.cpa.auto_2: 1
|
||||
cg_type.cpa.auto_3: 0
|
||||
cg_type_w0.cpa.auto_0: 1
|
||||
cg_type_w0.cpa.auto_1: 0
|
||||
cg_type_w0.cpa.auto_2: 0
|
||||
cg_type_w0.cpa.auto_3: 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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 option.weight and type_option.weight (IEEE 1800-2023 19.7, 19.11)
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
bit [1:0] a; // 4 automatic bins
|
||||
bit [2:0] b; // 8 automatic bins
|
||||
|
||||
// Instance coverage is the average of the items, weighted by their option.weight
|
||||
covergroup cg_item;
|
||||
cpa: coverpoint a {
|
||||
option.weight = 3;
|
||||
}
|
||||
cpb: coverpoint b;
|
||||
endgroup
|
||||
|
||||
// A zero weight removes an item from the instance coverage
|
||||
covergroup cg_item_zero;
|
||||
cpa: coverpoint a {
|
||||
option.weight = 0;
|
||||
}
|
||||
cpb: coverpoint b;
|
||||
endgroup
|
||||
|
||||
// Nothing contributes, so the covergroup's option.weight decides between 0 and 100
|
||||
covergroup cg_all_zero;
|
||||
cpa: coverpoint a {
|
||||
option.weight = 0;
|
||||
}
|
||||
cpb: coverpoint b {
|
||||
option.weight = 0;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Weights from a constructor argument and an expression, and a cross weight
|
||||
covergroup cg_cross(int w);
|
||||
option.weight = w + 1;
|
||||
cpa: coverpoint a {
|
||||
bins a0 = {0};
|
||||
bins a1 = {1};
|
||||
option.weight = w;
|
||||
}
|
||||
cpb: coverpoint b {
|
||||
bins b0 = {0};
|
||||
bins b1 = {1};
|
||||
}
|
||||
x: cross cpa, cpb{option.weight = 4;}
|
||||
endgroup
|
||||
|
||||
// The covergroup's own weights do not scale its coverage, and an item's
|
||||
// type_option.weight only weighs type coverage merged over the instances
|
||||
covergroup cg_group;
|
||||
option.weight = 5;
|
||||
type_option.weight = 2;
|
||||
type_option.merge_instances = 0;
|
||||
cpa: coverpoint a {
|
||||
type_option.weight = 7;
|
||||
}
|
||||
cpb: coverpoint b {
|
||||
type_option.weight = 0;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Type coverage is the average of the instances, weighted by their option.weight
|
||||
covergroup cg_type;
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
covergroup cg_type_w0;
|
||||
type_option.weight = 0;
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
// Never constructed
|
||||
covergroup cg_never;
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
covergroup cg_never_w0;
|
||||
type_option.weight = 0;
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
// Embedded covergroup weighted by a member of the enclosing class
|
||||
class Cls;
|
||||
int m_weight;
|
||||
covergroup cg_emb with function sample (bit [1:0] v);
|
||||
option.weight = m_weight;
|
||||
cpv: coverpoint v {
|
||||
option.weight = m_weight;
|
||||
}
|
||||
cpz: coverpoint v {
|
||||
bins zero = {0};
|
||||
}
|
||||
endgroup
|
||||
function new(int weight);
|
||||
m_weight = weight;
|
||||
cg_emb = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
cg_item c_item = new;
|
||||
cg_item_zero c_item_zero = new;
|
||||
cg_all_zero c_all_zero = new;
|
||||
cg_cross c_cross = new(3);
|
||||
cg_group c_group = new;
|
||||
cg_type t1, t2, t3;
|
||||
cg_type_w0 u1;
|
||||
Cls obj;
|
||||
|
||||
initial begin
|
||||
// Declaration values and defaults are visible (IEEE 1800-2023 19.10)
|
||||
`checkd(c_item.option.weight, 1);
|
||||
`checkd(c_item.type_option.weight, 1);
|
||||
`checkd(c_cross.option.weight, 4);
|
||||
`checkd(c_group.option.weight, 5);
|
||||
`checkd(c_group.type_option.weight, 2);
|
||||
`checkd(cg_group::type_option.weight, 2);
|
||||
`checkd(cg_never_w0::type_option.weight, 0);
|
||||
|
||||
a = 0;
|
||||
b = 0;
|
||||
c_item.sample();
|
||||
c_item_zero.sample();
|
||||
c_all_zero.sample();
|
||||
c_cross.sample();
|
||||
c_group.sample();
|
||||
a = 1;
|
||||
c_item.sample();
|
||||
c_item_zero.sample();
|
||||
c_all_zero.sample();
|
||||
c_cross.sample();
|
||||
c_group.sample();
|
||||
|
||||
// cpa: 2/4 automatic bins, cpb: 1/8 automatic bins
|
||||
`checkr(c_item.get_inst_coverage(), (3 * 50.0 + 12.5) / 4);
|
||||
`checkr(c_item_zero.get_inst_coverage(), 12.5);
|
||||
`checkr(c_all_zero.get_inst_coverage(), 0.0);
|
||||
`checkr(c_group.get_inst_coverage(), (50.0 + 12.5) / 2);
|
||||
// cpa: a0 and a1, cpb: b0, x: <a0,b0> and <a1,b0>
|
||||
`checkr(c_cross.get_inst_coverage(), (3 * 100.0 + 50.0 + 4 * 50.0) / 8);
|
||||
|
||||
// A procedural option.weight takes effect immediately
|
||||
c_all_zero.option.weight = 0;
|
||||
`checkr(c_all_zero.get_inst_coverage(), 100.0);
|
||||
c_all_zero.option.weight = 1;
|
||||
`checkr(c_all_zero.get_inst_coverage(), 0.0);
|
||||
|
||||
// An instance without coverage does not contribute to type coverage
|
||||
`checkr(cg_all_zero::get_coverage(), 0.0);
|
||||
`checkr(cg_item::get_coverage(), c_item.get_inst_coverage());
|
||||
`checkr(cg_group::get_coverage(), (50.0 + 12.5) / 2);
|
||||
|
||||
t1 = new;
|
||||
t2 = new;
|
||||
t3 = new;
|
||||
t1.option.weight = 3;
|
||||
a = 0;
|
||||
t1.sample();
|
||||
a = 1;
|
||||
t1.sample();
|
||||
a = 2;
|
||||
t2.sample();
|
||||
// t1 50%, t2 25%, and t3 0%
|
||||
`checkr(cg_type::get_coverage(), (3 * 50.0 + 25.0 + 0.0) / 5);
|
||||
t3.option.weight = 0;
|
||||
`checkr(t3.get_coverage(), (3 * 50.0 + 25.0) / 4);
|
||||
// A destroyed instance still contributes, with its last weight
|
||||
t2 = null;
|
||||
`checkr(cg_type::get_coverage(), (25.0 + 3 * 50.0) / 4);
|
||||
t1.option.weight = 0;
|
||||
`checkr(cg_type::get_coverage(), 25.0);
|
||||
|
||||
// With no contribution, type_option.weight decides between 0 and 100
|
||||
u1 = new;
|
||||
a = 0;
|
||||
u1.sample();
|
||||
`checkr(cg_type_w0::get_coverage(), 25.0);
|
||||
u1.option.weight = 0;
|
||||
`checkr(cg_type_w0::get_coverage(), 100.0);
|
||||
`checkr(cg_never::get_coverage(), 0.0);
|
||||
`checkr(cg_never_w0::get_coverage(), 100.0);
|
||||
cg_never::type_option.weight = 0;
|
||||
`checkr(cg_never::get_coverage(), 100.0);
|
||||
// type_option is shared by every instance
|
||||
cg_group::type_option.weight = 9;
|
||||
`checkd(c_group.type_option.weight, 9);
|
||||
|
||||
obj = new(2);
|
||||
`checkd(obj.cg_emb.option.weight, 2);
|
||||
obj.cg_emb.sample(0);
|
||||
`checkr(obj.cg_emb.get_inst_coverage(), (2 * 25.0 + 100.0) / 3);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,30 @@
|
||||
%Error: t/t_covergroup_weight_bad.v:13:21: Coverage option 'option.weight' is set to negative value '-1'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
: ... note: In instance 't'
|
||||
13 | option.weight = -1;
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_covergroup_weight_bad.v:14:26: Coverage option 'type_option.weight' is set to negative value '-2'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
: ... note: In instance 't'
|
||||
14 | type_option.weight = -2;
|
||||
| ^
|
||||
%Error: t/t_covergroup_weight_bad.v:16:23: Coverage option 'option.weight' is set to negative value '-3'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
: ... note: In instance 't'
|
||||
16 | option.weight = -3;
|
||||
| ^
|
||||
%Error: t/t_covergroup_weight_bad.v:19:28: Coverage option 'type_option.weight' is set to negative value '-4'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
: ... note: In instance 't'
|
||||
19 | type_option.weight = -4;
|
||||
| ^
|
||||
%Error: t/t_covergroup_weight_bad.v:21:41: Coverage option 'option.weight' is set to negative value '-5'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
: ... note: In instance 't'
|
||||
21 | x: cross cpa, cpb{option.weight = 2 - 7;}
|
||||
| ^
|
||||
%Error: t/t_covergroup_weight_bad.v:26:26: Coverage option 'type_option.weight' requires a constant expression (IEEE 1800-2023 19.7.1)
|
||||
: ... note: In instance 't'
|
||||
26 | type_option.weight = w;
|
||||
| ^
|
||||
%Error: t/t_covergroup_weight_bad.v:28:28: Coverage option 'type_option.weight' requires a constant expression (IEEE 1800-2023 19.7.1)
|
||||
: ... note: In instance 't'
|
||||
28 | type_option.weight = w;
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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
|
||||
|
||||
// Invalid coverage weights (IEEE 1800-2023 19.7, 19.7.1)
|
||||
|
||||
module t;
|
||||
bit [1:0] a;
|
||||
|
||||
covergroup cg_negative;
|
||||
option.weight = -1; // <--- Bad: negative
|
||||
type_option.weight = -2; // <--- Bad: negative
|
||||
cpa: coverpoint a {
|
||||
option.weight = -3; // <--- Bad: negative
|
||||
}
|
||||
cpb: coverpoint a {
|
||||
type_option.weight = -4; // <--- Bad: negative
|
||||
}
|
||||
x: cross cpa, cpb{option.weight = 2 - 7;} // <--- Bad: negative
|
||||
endgroup
|
||||
|
||||
covergroup cg_nonconst(int w);
|
||||
option.weight = w; // ok
|
||||
type_option.weight = w; // <--- Bad: not constant
|
||||
cpa: coverpoint a {
|
||||
type_option.weight = w; // <--- Bad: not constant
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg_negative cg_negative_inst = new;
|
||||
cg_nonconst cg_nonconst_inst = new(1);
|
||||
initial $finish;
|
||||
endmodule
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
test.top_filename = 't/t_covergroup_weight.v'
|
||||
|
||||
# Without --coverage, instances are freed when their last handle drops. Under --protect-ids,
|
||||
# get_coverage() must find the instances under the same obfuscated type name.
|
||||
test.compile(verilator_flags2=['--protect-ids', '--protect-key WEIGHT_KEY', '-Wno-INSECURE'])
|
||||
|
||||
test.execute()
|
||||
|
||||
for filename in test.glob_some(test.obj_dir + '/*.cpp'):
|
||||
test.file_grep_not(filename, r'cg_type|cg_never|cg_item')
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,10 @@
|
||||
%Error: t/t_covergroup_weight_run_bad.v:24: Coverage option 'option.weight' is set to negative value '-2'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
-Info: t/t_covergroup_weight_run_bad.v:24: Verilog $stop, ignored due to +verilator+error+limit
|
||||
%Error: t/t_covergroup_weight_run_bad.v:24: Coverage option 'option.weight' is set to negative value '-2'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:37: Coverage option 'option.weight' is set to negative value '-1'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:41: Coverage option 'option.weight' is set to negative value '-1'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:41: Coverage option 'option.weight' is set to negative value '-3'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:41: Coverage option 'option.weight' is set to negative value '-4'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:46: Coverage option 'option.weight' is set to negative value '-5'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
%Error: t/t_covergroup_weight_run_bad.v:49: Coverage option 'type_option.weight' is set to negative value '-6'; weights must be non-negative (IEEE 1800-2023 19.7)
|
||||
*-* All Finished *-*
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute(all_run_flags=['+verilator+error+limit+100'], expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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
|
||||
|
||||
// Coverage weights that are negative only at run time (IEEE 1800-2023 19.7): each
|
||||
// negative value is reported once, and counts as zero
|
||||
|
||||
// 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;
|
||||
bit [1:0] a; // 4 automatic bins
|
||||
bit [2:0] b; // 8 automatic bins
|
||||
|
||||
covergroup cg_item(int wa, int wb);
|
||||
cpa: coverpoint a {
|
||||
option.weight = wa;
|
||||
}
|
||||
cpb: coverpoint b {
|
||||
option.weight = wb; // <--- Bad: negative at run time
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_cross(int w);
|
||||
cpa: coverpoint a {
|
||||
bins a0 = {0};
|
||||
bins a1 = {1};
|
||||
}
|
||||
cpb: coverpoint b {
|
||||
bins b0 = {0};
|
||||
bins b1 = {1};
|
||||
}
|
||||
x: cross cpa, cpb{option.weight = w;} // <--- Bad: negative at run time
|
||||
endgroup
|
||||
|
||||
// A covergroup's own weights are reported at its declaration
|
||||
covergroup cg_inst(int w); // <--- Bad: option.weight negative at run time
|
||||
option.weight = w;
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
covergroup cg_empty; // <--- Bad: option.weight negative at run time
|
||||
endgroup
|
||||
|
||||
covergroup cg_never; // <--- Bad: type_option.weight negative at run time
|
||||
cpa: coverpoint a;
|
||||
endgroup
|
||||
|
||||
cg_item c_cancel, c_over;
|
||||
cg_cross c_cross;
|
||||
cg_inst d1, d2;
|
||||
cg_empty e1;
|
||||
|
||||
initial begin
|
||||
// Item weights summing to zero, or nearly, would otherwise give a zero
|
||||
// denominator, or coverage outside 0..100
|
||||
c_cancel = new(2, -2);
|
||||
c_over = new(3, -2);
|
||||
c_cross = new(-1);
|
||||
a = 0;
|
||||
b = 0;
|
||||
c_cancel.sample();
|
||||
c_over.sample();
|
||||
c_cross.sample();
|
||||
a = 1;
|
||||
c_cancel.sample();
|
||||
c_over.sample();
|
||||
c_cross.sample();
|
||||
// cpa: 2/4 automatic bins, and cpb weighs nothing
|
||||
`checkr(c_cancel.get_inst_coverage(), 50.0);
|
||||
`checkr(c_over.get_inst_coverage(), 50.0);
|
||||
// cpa: a0 and a1, cpb: b0, and x weighs nothing
|
||||
`checkr(c_cross.get_inst_coverage(), (100.0 + 50.0) / 2);
|
||||
|
||||
// Reported by the constructor
|
||||
d1 = new(-1);
|
||||
d2 = new(1);
|
||||
a = 0;
|
||||
d1.sample();
|
||||
a = 1;
|
||||
d1.sample();
|
||||
a = 2;
|
||||
d2.sample();
|
||||
// d1 50% weighs nothing, d2 25%
|
||||
`checkr(cg_inst::get_coverage(), 25.0);
|
||||
// A procedural assignment is reported when coverage is next computed, once
|
||||
d2.option.weight = -3;
|
||||
`checkr(d2.get_inst_coverage(), 25.0);
|
||||
`checkr(cg_inst::get_coverage(), 0.0);
|
||||
d2.option.weight = -4;
|
||||
`checkr(cg_inst::get_coverage(), 0.0);
|
||||
d2.option.weight = 2;
|
||||
`checkr(cg_inst::get_coverage(), 25.0);
|
||||
// A destroyed instance keeps weighing nothing
|
||||
d1 = null;
|
||||
`checkr(cg_inst::get_coverage(), 25.0);
|
||||
|
||||
// With nothing to cover, a negative weight decides as a zero weight
|
||||
e1 = new;
|
||||
e1.option.weight = -5;
|
||||
`checkr(e1.get_inst_coverage(), 100.0);
|
||||
`checkr(e1.get_inst_coverage(), 100.0);
|
||||
cg_never::type_option.weight = -6;
|
||||
`checkr(cg_never::get_coverage(), 100.0);
|
||||
`checkr(cg_never::get_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -791,6 +791,8 @@ module Vt_debug_emitv_t;
|
||||
logic [1:0] cg_sig2;
|
||||
covergroup Vt_debug_emitv_cg_basic;
|
||||
function new;
|
||||
option.weight = 'sh2;
|
||||
type_option.weight = 'sh3;
|
||||
cp_sig: coverpoint cg_sig {
|
||||
bins low = {['sh0:'sh3]} iff (cg_sig2[0]);
|
||||
bins high = {['sh4:'sh6]};
|
||||
@@ -800,9 +802,10 @@ module Vt_debug_emitv_t;
|
||||
illegal_bins ill = {'sh5};
|
||||
};
|
||||
cp_options: coverpoint cg_sig2 {
|
||||
|
||||
???? // COVEROPTION
|
||||
'sh2};
|
||||
option.at_least = 'sh2;
|
||||
option.weight = 'sh4;
|
||||
type_option.weight = 'sh5;
|
||||
};
|
||||
endfunction
|
||||
int signed __Vint;
|
||||
struct {
|
||||
@@ -959,6 +962,7 @@ module Vt_debug_emitv_t;
|
||||
};
|
||||
cx: cross cp_x, cp_y iff ((cg_sig[0] == cg_sig2[0]));
|
||||
cx_select: cross cp_x, cp_y {
|
||||
option.weight = 'sh2;
|
||||
bins entire = cx_select;
|
||||
bins plain = binsof(cp_x);
|
||||
bins named = binsof(cp_x.x0);
|
||||
|
||||
@@ -394,6 +394,7 @@ module t (/*AUTOARG*/
|
||||
covergroup cg_basic;
|
||||
option.per_instance = 1;
|
||||
option.weight = 2;
|
||||
type_option.weight = 3;
|
||||
cp_sig: coverpoint cg_sig {
|
||||
bins low = {[0:3]} iff (cg_sig2[0]);
|
||||
bins high = {[4:6]};
|
||||
@@ -405,6 +406,8 @@ module t (/*AUTOARG*/
|
||||
// Coverpoint with per-coverpoint option but no explicit bins
|
||||
cp_options: coverpoint cg_sig2 {
|
||||
option.at_least = 2;
|
||||
option.weight = 4;
|
||||
type_option.weight = 5;
|
||||
}
|
||||
endgroup
|
||||
|
||||
@@ -436,6 +439,7 @@ module t (/*AUTOARG*/
|
||||
}
|
||||
cx: cross cp_x, cp_y iff (cg_sig[0] == cg_sig2[0]);
|
||||
cx_select: cross cp_x, cp_y{
|
||||
option.weight = 2;
|
||||
bins entire = cx_select;
|
||||
bins plain = binsof (cp_x);
|
||||
bins named = binsof (cp_x.x0);
|
||||
|
||||
@@ -777,7 +777,8 @@
|
||||
%000001 initial begin
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
// Sample 2-way: hit all 4 combinations
|
||||
// cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count)
|
||||
// cg2: coverage is the mean of cp_addr (2 bins), cp_cmd (2 bins), and the cross (4 bins)
|
||||
// (IEEE 1800-2023 19.11)
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -788,7 +789,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg2_inst.sample(); // addr0 x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3); // addr0, read, one cross
|
||||
-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
|
||||
@@ -802,7 +803,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg2_inst.sample(); // addr1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3); // 2 cross bins
|
||||
-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
|
||||
@@ -816,7 +817,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg2_inst.sample(); // addr0 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3); // 3 cross bins
|
||||
-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
|
||||
@@ -830,13 +831,13 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg2_inst.sample(); // addr1 x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), 100.0); // 8/8: all 4 cross bins hit
|
||||
%000001 `checkr(cg2_inst.get_inst_coverage(), 100.0); // all 4 cross bins 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 3-way: hit 4 of 12 combinations
|
||||
// cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr)
|
||||
// cg3: 4 of 12 cross bins hit (not clean; no intermediate checkr)
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -871,7 +872,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
|
||||
// Sample 4-way: hit 4 of 16 combinations
|
||||
// cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50%
|
||||
// cg4: four 2-bin coverpoints and a 16-bin cross; 4 cross bins hit
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -892,7 +893,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg4_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins
|
||||
%000001 `checkr(cg4_inst.get_inst_coverage(), 72.5); // cp_mode 1/2, cross 2/16, others full
|
||||
-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
|
||||
@@ -916,20 +917,19 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg4_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins
|
||||
%000001 `checkr(cg4_inst.get_inst_coverage(), 85.0); // all cp bins, cross 4/16
|
||||
-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 cg5 (cross with option.weight=2; weight is ignored in flat bin count)
|
||||
// cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// Sample cg5 (cross with option.weight=2): the cross counts twice in the mean
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg5_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
%000001 `checkr(cg5_inst.get_inst_coverage(), (50.0 + 50.0 + 2 * 25.0) / 4); // addr0, read, 1/4
|
||||
-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
|
||||
@@ -939,20 +939,20 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg5_inst.sample();
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
%000001 `checkr(cg5_inst.get_inst_coverage(), (100.0 + 100.0 + 2 * 50.0) / 4); // cross 2/4
|
||||
-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_ignore: addr=3 is in ignore_bins so no cross bins for it
|
||||
// cg_ignore: 2+2+4=8 bins total
|
||||
// cg_ignore: 2-bin coverpoints and a 4-bin cross
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_ignore_inst.sample(); // a0 x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
-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
|
||||
@@ -962,7 +962,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_ignore_inst.sample(); // a1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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
|
||||
@@ -972,7 +972,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_ignore_inst.sample(); // a0 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
-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
|
||||
@@ -982,7 +982,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_ignore_inst.sample(); // a1 x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 100.0);
|
||||
-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
|
||||
@@ -998,14 +998,14 @@
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
// Sample range-bin cross
|
||||
// cg_range: 2+2+4=8 bins
|
||||
// cg_range: 2-bin coverpoints and a 4-bin cross
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_range_inst.sample(); // lo_range x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
-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
|
||||
@@ -1015,7 +1015,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_range_inst.sample(); // hi_range x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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
|
||||
@@ -1025,7 +1025,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_range_inst.sample(); // lo_range x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
-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
|
||||
@@ -1035,13 +1035,13 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_range_inst.sample(); // hi_range x read
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
%000001 `checkr(cg_range_inst.get_inst_coverage(), 100.0);
|
||||
-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_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins)
|
||||
// cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75%
|
||||
// cg_at_least: 2 of 4 cross bins hit (count=1, at_least effectively 1)
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -1054,13 +1054,13 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_at_least_inst.sample(); // addr1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_at_least_inst.get_inst_coverage(), 75.0);
|
||||
%000001 `checkr(cg_at_least_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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_goal (option.goal in cross body; does not affect hit counting)
|
||||
// cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_goal: 2 of 4 cross bins hit
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -1073,13 +1073,13 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_goal_inst.sample(); // addr1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_goal_inst.get_inst_coverage(), 75.0);
|
||||
%000001 `checkr(cg_goal_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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_unsup_cross_opt
|
||||
// cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_unsup_cross_opt: 2 of 4 cross bins hit; the dropped cross contributes nothing
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -1092,13 +1092,13 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_unsup_cross_opt_inst.sample(); // addr1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), 75.0);
|
||||
%000001 `checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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_unnamed_cross
|
||||
// cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
// cg_unnamed_cross: 2 of 4 cross bins hit
|
||||
%000001 addr = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cmd = 0;
|
||||
@@ -1111,17 +1111,17 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_unnamed_cross_inst.sample(); // a1 x write
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0);
|
||||
%000001 `checkr(cg_unnamed_cross_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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_mixed: 10 bins total (cp_addr 2 + cp_cmd 2 + cp_solo 2 + cross ab 4)
|
||||
// Sample cg_mixed: cp_addr, cp_cmd, and cp_solo have 2 bins each, and cross ab 4
|
||||
%000001 addr = 0; cmd = 0; mode = 0;
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_mixed_inst.sample(); // addr0, read, solo normal, ab(addr0_x_read)
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_mixed_inst.get_inst_coverage(), 40.0); // 4/10
|
||||
%000001 `checkr(cg_mixed_inst.get_inst_coverage(), (50.0 + 50.0 + 50.0 + 25.0) / 4);
|
||||
-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
|
||||
@@ -1137,7 +1137,7 @@
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 cg_mixed_inst.sample(); // addr1, write, ab(addr1_x_write)
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
%000001 `checkr(cg_mixed_inst.get_inst_coverage(), 100.0); // 10/10
|
||||
%000001 `checkr(cg_mixed_inst.get_inst_coverage(), 100.0);
|
||||
-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
|
||||
@@ -1287,7 +1287,7 @@
|
||||
-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%
|
||||
// cg_trans: t01_x_v6 never completes, so only the cross is partial (1/2)
|
||||
// __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
|
||||
@@ -1295,34 +1295,34 @@
|
||||
-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 `checkr(cg_trans_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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)
|
||||
// cg_be: 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 `checkr(cg_be_inst.get_inst_coverage(), (50.0 + 50.0 + 25.0) / 3);
|
||||
-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 `checkr(cg_be_inst.get_inst_coverage(), (100.0 + 100.0 + 50.0) / 3);
|
||||
-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 `checkr(cg_be_inst.get_inst_coverage(), (100.0 + 100.0 + 75.0) / 3);
|
||||
-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 `checkr(cg_be_inst.get_inst_coverage(), 100.0);
|
||||
-000001 point: type=line comment=block hier=top.t
|
||||
-000000 point: type=line comment=block hier=top.t
|
||||
-000001 point: type=line comment=else hier=top.t
|
||||
|
||||
Reference in New Issue
Block a user