Support covergroups, coverpoints, and bins (#784) (#7117)

Fixes #784.
This commit is contained in:
Matthew Ballance 2026-06-05 06:35:01 -07:00 committed by GitHub
parent 7e2fe64ae2
commit 2886291eba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
163 changed files with 7575 additions and 1123 deletions

View File

@ -133,9 +133,13 @@ verilator_coverage Arguments
.. option:: --filter-type <regex>
Skips records of coverage types that matches with <regex>
Possible values are `toggle`, `line`, `branch`, `expr`, `user`,
`fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The default
value is `\*`.
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
default value is `\*`.
The `covergroup` type represents SystemVerilog functional coverage
including covergroups, coverpoints, bins, and cross coverage as defined
in IEEE 1800-2023 Section 19.
.. option:: --help

View File

@ -40,8 +40,10 @@ union, var, void, priority case/if, and unique case/if.
It also supports .name and .\* interconnection.
Verilator partially supports concurrent assert and cover statements; see
the enclosed coverage tests for the allowed syntax.
Verilator partially supports concurrent assert and cover statements, as well as
SystemVerilog functional coverage with ``covergroup``, ``coverpoint``, bins,
cross coverage, and transition bins. See :ref:`Functional Coverage<user
coverage>` for details.
Verilator has limited support for class and related object-oriented
constructs.
@ -363,10 +365,15 @@ appropriate width.
Assertions
----------
Verilator is beginning to add support for assertions. Verilator currently
only converts assertions to simple ``if (...) error`` statements, and
coverage statements to increment the line counters described in the
coverage section.
Verilator partially supports assertions and functional coverage.
Verilator currently converts assertions to simple ``if (...) error`` statements,
and simple coverage statements to increment the line counters described in the
:ref:`coverage section<Coverage>`.
Verilator also partially supports SystemVerilog functional coverage with
``covergroup``, ``coverpoint``, bins, cross coverage, and transition bins. See
:ref:`Functional Coverage<user coverage>` for details on using
covergroups for comprehensive coverage analysis.
Verilator does not support SEREs yet. All assertion and coverage statements
must be simple expressions that complete in one cycle.

View File

@ -184,7 +184,8 @@ Verilator supports adding code to the Verilated model to support
SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables
all forms of coverage:
- :ref:`User Coverage`
- :ref:`Property Coverage`
- :ref:`Covergroup Coverage`
- :ref:`FSM Coverage`
- :ref:`Line Coverage`
- :ref:`Toggle Coverage`
@ -193,22 +194,34 @@ When a model with coverage is executed, it will create a coverage file for
collection and later analysis, see :ref:`Coverage Collection`.
.. _user coverage:
.. _property coverage:
Functional Coverage
-------------------
Property Coverage
-----------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate functional coverage points the user has inserted manually in
SystemVerilog code through into the Verilated model.
translate property coverage points the user has inserted manually in
SystemVerilog code into the Verilated model.
For example, the following SystemVerilog statement will add a coverage
point under the coverage name "DefaultClock":
For simple coverage points, use the ``cover property`` construct:
.. code-block:: sv
DefaultClock: cover property (@(posedge clk) cyc==3);
This adds a coverage point that tracks whether the condition has been observed.
.. _covergroup coverage:
Covergroup Coverage
-------------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate covergroup coverage points the user has inserted manually in
SystemVerilog code into the Verilated model. Verilator supports
coverpoints with value and transition bins, and cross points.
.. _fsm coverage:
FSM Coverage
@ -301,7 +314,6 @@ Annotated output produced by :command:`verilator_coverage --annotate` will
label FSM points with `fsm_state` and `fsm_arc`, and synthetic fallback
transitions with `SYNTHETIC DEFAULT ARC`.
.. _line coverage:
Line Coverage

View File

@ -39,6 +39,7 @@ VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'd
VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
// Bin attributes
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
@ -52,6 +53,7 @@ VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'd
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
#define VL_CIK_COLUMN "n"
#define VL_CIK_COMMENT "o"
#define VL_CIK_CROSS_BINS "Cb"
#define VL_CIK_FILENAME "f"
#define VL_CIK_FSM_FROM "Ff"
#define VL_CIK_FSM_TAG "Fg"
@ -77,6 +79,7 @@ public:
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
if (key == "column") return VL_CIK_COLUMN;
if (key == "comment") return VL_CIK_COMMENT;
if (key == "cross_bins") return VL_CIK_CROSS_BINS;
if (key == "filename") return VL_CIK_FILENAME;
if (key == "fsm_from") return VL_CIK_FSM_FROM;
if (key == "fsm_tag") return VL_CIK_FSM_TAG;

View File

@ -70,6 +70,7 @@ set(HEADERS
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Covergroup.h
V3Dead.h
V3DebugBisect.h
V3Delayed.h
@ -202,6 +203,7 @@ set(HEADERS
V3WidthCommit.h
V3WidthRemove.h
VlcBucket.h
VlcCovergroup.h
VlcOptions.h
VlcPoint.h
VlcSource.h
@ -237,6 +239,7 @@ set(COMMON_SOURCES
V3Const__gen.cpp
V3Coverage.cpp
V3CoverageJoin.cpp
V3Covergroup.cpp
V3Dead.cpp
V3Delayed.cpp
V3Depth.cpp

View File

@ -249,6 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Combine.o \
V3Common.o \
V3Coverage.o \
V3Covergroup.o \
V3CoverageJoin.o \
V3Dead.o \
V3Delayed.o \

View File

@ -638,11 +638,150 @@ public:
~ActiveVisitor() override = default;
};
//######################################################################
// Pass 1: collect sample CFuncs and sampling events from covergroup class scopes
class CovergroupCollectVisitor final : public VNVisitor {
// NODE STATE
// Netlist:
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
// STATE
AstClass* m_classp = nullptr; // Current covergroup class context, or nullptr
// VISITORS
void visit(AstClass* nodep) override {
if (!nodep->isCovergroup()) return;
VL_RESTORER(m_classp);
m_classp = nodep;
iterateChildren(nodep);
}
void visit(AstScope* nodep) override { iterateChildren(nodep); }
void visit(AstCFunc* nodep) override {
if (!m_classp) return;
if (nodep->isCovergroupSample()) m_classp->user1p(nodep);
}
void visit(AstCovergroup* nodep) override {
// V3Covergroup guarantees: only supported-event covergroups survive to V3Active,
// and they are always inside a covergroup class (so m_classp is set).
// Unlink eventp from cgp so it survives cgp's deletion,
// then store it in user2p for use during the second pass.
m_classp->user2p(nodep->eventp()->unlinkFrBack());
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CovergroupCollectVisitor(AstNetlist* nodep) { iterate(nodep); }
~CovergroupCollectVisitor() override = default;
};
//######################################################################
// Pass 2: inject automatic sample() calls for covergroup instances
class CovergroupInjectVisitor final : public VNVisitor {
// NODE STATE (set by CovergroupCollectVisitor, consumed here)
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
// STATE
ActiveNamer m_namer; // Reuse active naming infrastructure
// VISITORS
void visit(AstScope* nodep) override {
m_namer.main(nodep); // Initialize active naming for this scope
iterateChildren(nodep);
}
void visit(AstVarScope* nodep) override {
// Get the underlying var
AstVar* const varp = nodep->varp();
UASSERT_OBJ(varp, nodep, "AstVarScope must have non-null varp");
// Check if the variable is of covergroup class type
const AstNodeDType* const dtypep = varp->dtypep();
UASSERT_OBJ(dtypep, nodep, "AstVar must have non-null dtypep after V3Width");
const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType);
if (!classRefp) return;
AstClass* const classp = classRefp->classp();
// Check if this covergroup has an automatic sampling event
AstSenTree* const eventp = VN_CAST(classp->user2p(), SenTree);
if (!eventp) return; // No automatic sampling for this covergroup
// V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc
AstCFunc* const sampleCFuncp = VN_AS(classp->user1p(), CFunc);
UASSERT_OBJ(sampleCFuncp, nodep,
"No sample() CFunc found for covergroup " << classp->name());
// Create a VarRef to the covergroup instance for the method call
FileLine* const fl = nodep->fileline();
AstVarRef* const varrefp = new AstVarRef{fl, nodep, VAccess::READ};
// Create the CMethodCall to sample()
// Note: We don't pass arguments in argsp since vlSymsp is passed via argTypes
AstCMethodCall* const cmethodCallp
= new AstCMethodCall{fl, varrefp, sampleCFuncp, nullptr};
cmethodCallp->dtypeSetVoid();
cmethodCallp->argTypes("vlSymsp");
// Clone the sensitivity for this active block.
// V3Scope has already resolved all VarRefs in eventp, so the clone
// inherits correct varScopep values with no fixup needed.
AstSenTree* senTreep = eventp->cloneTree(false);
// Get or create the AstActive node for this sensitivity
// senTreep is a template used by getActive() which clones it into the AstActive;
// delete it afterwards as it is not added to the AST directly.
AstActive* const activep = m_namer.getActive(fl, senTreep);
VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
// Wrap the sample() call in an AstAlways so SchedPartition handles it
// via visit(AstNodeProcedure*) like any other clocked always block.
activep->addStmtsp(
new AstAlways{fl, VAlwaysKwd::ALWAYS_FF, nullptr, cmethodCallp->makeStmt()});
}
void visit(AstClass* nodep) override {
iterateChildren(nodep);
// Delete the owned sampling event template stored during collection
if (AstSenTree* const eventp = VN_CAST(nodep->user2p(), SenTree)) {
VL_DO_DANGLING(pushDeletep(eventp), eventp);
}
}
void visit(AstActive*) override {} // Don't iterate into actives
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CovergroupInjectVisitor(AstNetlist* nodep) { iterate(nodep); }
~CovergroupInjectVisitor() override = default;
};
//######################################################################
// Active class functions
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ ActiveVisitor{nodep}; } // Destruct before checking
if (v3Global.useCovergroup()) {
// Add automatic covergroup sampling in two focused passes.
// user1p/user2p on AstClass span both passes; guards must outlive both visitors.
const VNUser1InUse user1InUse;
const VNUser2InUse user2InUse;
CovergroupCollectVisitor{nodep}; // Pass 1: collect CFuncs and events into user#p
CovergroupInjectVisitor{nodep}; // Pass 2: inject sample() calls, delete user2p events
}
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -1149,6 +1149,120 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
//######################################################################
class VCoverBinsType final {
public:
enum en : uint8_t {
BINS_ARRAY, // Array of bins with user-speciifed size
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
BINS_DEFAULT, // Default bin
BINS_IGNORE, // Ignore bin
BINS_ILLEGAL, // Illegal bin
BINS_TRANSITION, // Transition bin
BINS_USER, // Single bin with one or more values/ranges
BINS_WILDCARD // Wildcard bin
};
enum en m_e;
VCoverBinsType() // LCOV_EXCL_START
: m_e{BINS_USER} {} // LCOV_EXCL_STOP
// cppcheck-suppress noExplicitConstructor
constexpr VCoverBinsType(en _e)
: m_e{_e} {}
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
const char* ascii() const {
static const char* const names[]
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
return names[m_e];
}
};
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
return lhs.m_e == rhs;
}
//######################################################################
class VCoverOptionType final {
public:
enum en : uint8_t {
// Shared by option.* and type_option.*
WEIGHT,
GOAL,
AT_LEAST,
AUTO_BIN_MAX,
PER_INSTANCE,
COMMENT,
// option.* only (IEEE 1800-2023 Table 19-1)
NAME,
CROSS_NUM_PRINT_MISSING,
CROSS_RETAIN_AUTO_BINS,
DETECT_OVERLAP,
GET_INST_COVERAGE,
// type_option.* only (IEEE 1800-2023 Table 19-3)
STROBE,
MERGE_INSTANCES,
DISTRIBUTE_FIRST,
REAL_INTERVAL,
// sentinel - should never appear after parse-time validation
UNKNOWN
};
enum en m_e;
// cppcheck-suppress noExplicitConstructor
constexpr VCoverOptionType(en _e)
: m_e{_e} {}
const char* ascii() const {
static const char* const names[] = {"weight",
"goal",
"at_least",
"auto_bin_max",
"per_instance",
"comment",
"name",
"cross_num_print_missing",
"cross_retain_auto_bins",
"detect_overlap",
"get_inst_coverage",
"strobe",
"merge_instances",
"distribute_first",
"real_interval",
"unknown"};
return names[m_e];
}
};
constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) {
return lhs.m_e == rhs;
}
//######################################################################
class VTransRepType final {
public:
enum en : uint8_t {
NONE, // No repetition
CONSEC, // Consecutive repetition [*]
GOTO, // Goto repetition [->]
NONCONS // Nonconsecutive repetition [=]
};
enum en m_e;
VTransRepType() // LCOV_EXCL_START
: m_e{NONE} {} // LCOV_EXCL_STOP
// cppcheck-suppress noExplicitConstructor
constexpr VTransRepType(en _e)
: m_e{_e} {}
explicit VTransRepType(int _e) // LCOV_EXCL_START
: m_e(static_cast<en>(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
const char* ascii() const {
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
return names[m_e];
}
const char* asciiJson() const {
static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
return names[m_e];
}
};
//######################################################################
class VDirection final {
public:
enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF };

View File

@ -120,6 +120,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_verilogTask : 1; // Declared by user as task (versus internal-made)
bool m_virtual : 1; // Virtual method in class
bool m_needProcess : 1; // Needs access to VlProcess of the caller
bool m_isCovergroupSample : 1; // Covergroup sample() method
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
VLifetime m_lifetime; // Default lifetime of local vars
VIsCached m_purity; // Pure state
@ -151,7 +152,8 @@ protected:
, m_verilogFunction{false}
, m_verilogTask{false}
, m_virtual{false}
, m_needProcess{false} {
, m_needProcess{false}
, m_isCovergroupSample{false} {
addStmtsp(stmtsp);
cname(name); // Might be overridden by dpi import/export
}
@ -225,6 +227,8 @@ public:
void isVirtual(bool flag) { m_virtual = flag; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
bool isCovergroupSample() const { return m_isCovergroupSample; }
void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; }
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
VBaseOverride baseOverride() const { return m_baseOverride; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
@ -260,6 +264,20 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode {
// Base class for functional coverage items (coverpoints, crosses)
protected:
string m_name; // Item name
public:
AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name)
: AstNode{t, fl}
, m_name{name} {}
ASTGEN_MEMBERS_AstNodeFuncCovItem;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool maybePointedTo() const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
@ -521,6 +539,7 @@ class AstCFunc final : public AstNode {
bool m_recursive : 1; // Recursive or part of recursion
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
// state
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
int m_cost; // Function call cost
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
@ -551,6 +570,7 @@ public:
m_dpiImportWrapper = false;
m_recursive = false;
m_noLife = false;
m_isCovergroupSample = false;
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
}
ASTGEN_MEMBERS_AstCFunc;
@ -626,6 +646,8 @@ public:
bool recursive() const { return m_recursive; }
void noLife(bool flag) { m_noLife = flag; }
bool noLife() const { return m_noLife; }
bool isCovergroupSample() const { return m_isCovergroupSample; }
void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; }
void cost(int cost) { m_cost = cost; }
// Special methods
bool emptyBody() const {
@ -767,13 +789,16 @@ public:
class AstCgOptionAssign final : public AstNode {
// A covergroup set of option
// Parents: CLASS(covergroup) or cross
string m_name; // Option name
const VCoverOptionType m_optType; // Option type
const string m_name; // Original option name (for diagnostics on unknown options)
const bool m_typeOption; // type_option vs option
// @astgen op1 := valuep : AstNodeExpr
public:
AstCgOptionAssign(FileLine* fl, bool typeOption, const string& name, AstNodeExpr* valuep)
AstCgOptionAssign(FileLine* fl, bool typeOption, VCoverOptionType optType,
const string& rawName, AstNodeExpr* valuep)
: ASTGEN_SUPER_CgOptionAssign(fl)
, m_name{name}
, m_optType{optType}
, m_name{rawName}
, m_typeOption{typeOption} {
this->valuep(valuep);
}
@ -781,7 +806,8 @@ public:
// ACCESSORS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name
string name() const override VL_MT_STABLE { return m_name; }
VCoverOptionType optionType() const { return m_optType; }
bool typeOption() const { return m_typeOption; }
};
class AstClassExtends final : public AstNode {
@ -1029,6 +1055,145 @@ public:
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstCoverBin final : public AstNode {
// Captures data for a coverpoint 'bins' declaration
// @astgen op1 := rangesp : List[AstNode]
// @astgen op2 := iffp : Optional[AstNodeExpr]
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
// @astgen op4 := transp : List[AstCoverTransSet]
const string m_name; // Base name of the bin
const VCoverBinsType m_type; // Bin type (eg AUTO, IGNORE, ILLEGAL)
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
: VCoverBinsType::BINS_USER))}
, m_isWildcard{isWildcard} {
addRangesp(rangesp);
}
// Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{VCoverBinsType::BINS_AUTO}
, m_isArray{true} {
this->arraySizep(arraySizep);
}
// Constructor for default bins (catch-all)
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{type} {}
// Constructor for transition bins
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp,
VCoverBinsType type = VCoverBinsType::BINS_TRANSITION, bool isArrayBin = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{type}
, m_isArray{isArrayBin} {
UASSERT(transp, "AstCoverBin transition constructor requires non-null transp");
addTransp(transp);
}
ASTGEN_MEMBERS_AstCoverBin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
VCoverBinsType binsType() const { return m_type; }
bool isWildcard() const { return m_isWildcard; }
bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; }
};
class AstCoverOption final : public AstNode {
// Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr
const VCoverOptionType m_type; // Option being assigned
public:
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl)
, m_type{type} {
this->valuep(valuep);
}
ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VCoverOptionType optionType() const { return m_type; }
};
class AstCoverTransItem final : public AstNode {
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
// @astgen op1 := valuesp : List[AstNode]
// @astgen op2 := repMinp : Optional[AstNodeExpr]
// @astgen op3 := repMaxp : Optional[AstNodeExpr]
const VTransRepType m_repType;
public:
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
: ASTGEN_SUPER_CoverTransItem(fl)
, m_repType{repType} {
addValuesp(valuesp);
}
ASTGEN_MEMBERS_AstCoverTransItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverTransSet final : public AstNode {
// Represents a transition set: value1 => value2 => value3
// @astgen op1 := itemsp : List[AstCoverTransItem]
public:
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
: ASTGEN_SUPER_CoverTransSet(fl) {
addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverTransSet;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCovergroup final : public AstNode {
// Represents a covergroup declaration. V3LinkParse transforms this
// into an AstClass with isCovergroup==true and attaches the clocking
// event as a new AstCovergroup sentinel in class membersp.
// @astgen op1 := argsp : List[AstVar]
// @astgen op2 := membersp : List[AstNode]
// @astgen op3 := eventp : Optional[AstSenTree]
// @astgen op4 := sampleArgsp : List[AstVar]
string m_name; // covergroup name
public:
AstCovergroup(FileLine* fl, const string& name, AstVar* argsp, AstVar* sampleArgsp,
AstNode* membersp, AstSenTree* eventp)
: ASTGEN_SUPER_Covergroup(fl)
, m_name{name} {
addArgsp(argsp);
addSampleArgsp(sampleArgsp);
addMembersp(membersp);
this->eventp(eventp);
}
ASTGEN_MEMBERS_AstCovergroup;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool maybePointedTo() const override { return true; }
};
class AstCoverpointRef final : public AstNode {
// Reference to a coverpoint used in a cross
const string m_name; // coverpoint name
public:
AstCoverpointRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverpointRef(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstCoverpointRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
};
class AstDefParam final : public AstNode {
// A defparam assignment
// Parents: MODULE
@ -2602,6 +2767,39 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
// // post-LinkParse: empty
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
: ASTGEN_SUPER_CoverCross(fl, name) {
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverCross;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverpoint final : public AstNodeFuncCovItem {
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := binsp : List[AstNode] // Parse: mixed AstCoverBin/AstCgOptionAssign;
// post-LinkParse: AstCoverBin only
// @astgen op3 := iffp : Optional[AstNodeExpr]
// @astgen op4 := optionsp : List[AstCoverOption]
public:
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp,
AstNodeExpr* iffp = nullptr, AstNode* binsp = nullptr)
: ASTGEN_SUPER_Coverpoint(fl, name) {
this->exprp(exprp);
this->iffp(iffp);
addBinsp(binsp);
}
ASTGEN_MEMBERS_AstCoverpoint;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
// === AstNodeGen ===
class AstGenBlock final : public AstNodeGen {
@ -2685,6 +2883,8 @@ class AstClass final : public AstNodeModule {
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
// Covergroup options (when m_covergroup is true)
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
public:
AstClass(FileLine* fl, const string& name, const string& libname)
@ -2714,6 +2914,9 @@ public:
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
void markPrintedFrom() { m_printedFrom = true; }
bool isPrintedFrom() const { return m_printedFrom; }
// Covergroup options accessors
int cgAutoBinMax() const { return m_cgAutoBinMax; }
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
// Return true if this class is an extension of base class (SLOW)
// Accepts nullptrs
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);

View File

@ -3732,3 +3732,54 @@ void AstWith::dumpJson(std::ostream& str) const {
}
dumpJsonGen(str);
}
//######################################################################
// Functional coverage dump methods
void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverBin::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_type.ascii();
if (m_isArray) str << "[]";
}
void AstCoverBin::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
if (m_isArray) str << ", \"isArray\": true";
}
void AstCoverTransItem::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (m_repType != VTransRepType::NONE) str << " " << m_repType.ascii();
}
void AstCoverTransItem::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"repType\": " << m_repType.asciiJson();
}
void AstCoverTransSet::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverOption::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_type.ascii();
}
void AstCoverOption::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"optionType\": \"" << m_type.ascii() << "\"";
}
void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstCoverpointRef::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }

View File

@ -288,6 +288,18 @@ class CoverageVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstClass* nodep) override {
VL_RESTORER(m_modp);
VL_RESTORER(m_state);
VL_RESTORER(m_exprTempNames);
VL_RESTORER(m_funcTemps);
createHandle(nodep);
m_modp = nodep;
// Covergroup declarations are not executable statements; suppress line/expr/toggle
// coverage so declarative elements (covergroup, coverpoint, cross) are not annotated
m_state.m_inModOff = nodep->isCovergroup();
iterateChildren(nodep);
}
void visit(AstAlways* nodep) override {
if (nodep->keyword() == VAlwaysKwd::CONT_ASSIGN) {
// Handle continuous assigns for expression coverage (but not line coverage)
@ -806,7 +818,6 @@ class CoverageVisitor final : public VNVisitor {
pair.first->second = varp;
if (m_ftaskp) {
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
} else {
m_modp->stmtsp()->addHereThisAsNext(varp);

1665
src/V3Covergroup.cpp Normal file

File diff suppressed because it is too large Load Diff

30
src/V3Covergroup.h Normal file
View File

@ -0,0 +1,30 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Covergroup implementation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3COVERGROUP_H_
#define VERILATOR_V3COVERGROUP_H_
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
class V3Covergroup final {
public:
static void covergroup(AstNetlist* nodep);
};
#endif // Guard

View File

@ -307,6 +307,72 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
void visit(AstCoverInc*) override {} // N/A
void visit(AstCoverToggle*) override {} // N/A
void visit(AstCovergroup* nodep) override {
// AstCovergroup appears as a member inside the lowered AstClass body.
// The outer covergroup/endcovergroup wrapper is already emitted by the
// AstNodeModule visitor (verilogKwd()="covergroup" on AstClass::isCovergroup).
// Here we only emit the clocking event, if any.
if (nodep->eventp()) {
putfs(nodep, "");
iterateConst(nodep->eventp());
}
}
void visit(AstCoverpoint* nodep) override {
putfs(nodep, nodep->name() + ": coverpoint ");
iterateAndNextConstNull(nodep->exprp());
if (nodep->binsp() || nodep->optionsp()) {
puts(" {\n");
iterateAndNextConstNull(nodep->optionsp());
iterateAndNextConstNull(nodep->binsp());
puts("}");
}
puts(";\n");
}
void visit(AstCoverBin* nodep) override {
switch (nodep->binsType()) {
case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break;
case VCoverBinsType::BINS_ILLEGAL: putfs(nodep, "illegal_bins "); break;
default: putfs(nodep, "bins "); break;
}
puts(nodep->name());
if (nodep->binsType() == VCoverBinsType::BINS_DEFAULT) {
puts(" = default");
} else if (nodep->transp()) {
puts(" = ");
for (AstNode* setp = nodep->transp(); setp; setp = setp->nextp()) {
if (setp != nodep->transp()) puts(", ");
iterateConst(setp);
}
} else if (nodep->rangesp()) { // LCOV_EXCL_BR_LINE - false: CoverBin always has
// transp/rangesp/default
puts(" = {");
for (AstNode* rangep = nodep->rangesp(); rangep; rangep = rangep->nextp()) {
if (rangep != nodep->rangesp()) puts(", ");
iterateConst(rangep);
}
puts("}");
}
puts(";\n");
}
void visit(AstCoverpointRef* nodep) override { putfs(nodep, nodep->name()); }
void visit(AstCoverCross* nodep) override {
putfs(nodep, nodep->name() + ": cross ");
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (itemp != nodep->itemsp()) puts(", ");
iterateConst(itemp);
}
puts(";\n");
}
void visit(AstCoverTransSet* nodep) override {
puts("(");
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (itemp != nodep->itemsp()) puts(" => ");
iterateConst(itemp);
}
puts(")");
}
void visit(AstCoverTransItem* nodep) override { iterateChildrenConst(nodep); }
void visit(AstCvtPackString* nodep) override {
putfs(nodep, "");
if (AstConst* const lhsConstp = VN_CAST(nodep->lhsp(), Const)) {
@ -745,6 +811,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateAndNextConstNull(nodep->elsep());
puts(")");
}
void visit(AstInsideRange* nodep) override {
puts("[");
iterateAndNextConstNull(nodep->lhsp());
puts(":");
iterateAndNextConstNull(nodep->rhsp());
puts("]");
}
void visit(AstRange* nodep) override {
puts("[");
if (VN_IS(nodep->leftp(), Const) && VN_IS(nodep->rightp(), Const)) {
@ -942,6 +1015,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
}
}
void visit(AstClassRefDType* nodep) override {
UASSERT_OBJ(nodep->classp(), nodep, "AstClassRefDType not linked");
putfs(nodep, EmitCUtil::prefixNameProtect(nodep->classp()));
}
void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); }
void visit(AstModport* nodep) override {
puts(nodep->verilogKwd());

View File

@ -132,6 +132,7 @@ class V3Global final {
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
bool m_useParallelBuild = false; // Use parallel build for model
bool m_useRandSequence = false; // Has `randsequence`
bool m_useCovergroup = false; // Has covergroup declarations
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
bool m_hasPrintedObjects = false; // Design has format args printed with to_string()
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
@ -220,6 +221,8 @@ public:
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useRandSequence() const { return m_useRandSequence; }
void useRandSequence(bool flag) { m_useRandSequence = flag; }
bool useCovergroup() const { return m_useCovergroup; }
void useCovergroup(bool flag) { m_useCovergroup = flag; }
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
bool hasPrintedObjects() const { return m_hasPrintedObjects; }

View File

@ -64,6 +64,7 @@ class LinkParseVisitor final : public VNVisitor {
AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew
AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew
int m_anonUdpId = 0; // Counter for anonymous UDP instances
int m_coverpointNum = 0; // Counter for unnamed coverpoints within current covergroup
int m_genblkAbove = 0; // Begin block number of if/case/for above
int m_genblkNum = 0; // Begin block number, 0=none seen
int m_beginDepth = 0; // How many begin blocks above current node within current AstNodeModule
@ -1149,6 +1150,268 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
// Append, for each arg in argsp, an INPUT parameter plus a "this.<member> = <param>"
// assignment to funcp. The parameter is a clone of the covergroup member and so shares its
// name; 'this.' on the LHS targets the member, otherwise the same-named local parameter
// shadows it and the assignment self-assigns the parameter, leaving the member unwritten.
// argsp may be null (no args appended).
static void addArgMemberCopies(AstFunc* funcp, AstNode* argsp) {
for (AstNode* argp = argsp; argp; argp = argp->nextp()) {
AstVar* const origVarp = VN_AS(argp, Var);
AstVar* const paramp = origVarp->cloneTree(false);
paramp->funcLocal(true);
paramp->direction(VDirection::INPUT);
funcp->addStmtsp(paramp);
AstNodeExpr* const lhsp = new AstDot{
origVarp->fileline(), false, new AstParseRef{origVarp->fileline(), "this"},
new AstParseRef{origVarp->fileline(), origVarp->name()}};
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
}
// Create boilerplate covergroup methods on the given AstClass.
// argsp/sampleArgsp are the raw arg lists still owned by the caller; they are iterated
// (cloned) but not deleted here.
static void createCovergroupMethods(AstClass* nodep, AstFunc* newFuncp, AstNode* argsp,
AstNode* sampleArgsp) {
// Hidden static to take unspecified reference argument results
AstVar* const defaultVarp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
nodep->addStmtsp(defaultVarp);
// Handle constructor arguments - add function parameters and assignments
if (argsp) {
UASSERT_OBJ(newFuncp, nodep,
"Covergroup class must have a 'new' constructor function");
// Save the existing body statements and unlink them, so the arg assignments run
// before the coverage body, then re-append the body.
AstNode* const existingBodyp = newFuncp->stmtsp();
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
addArgMemberCopies(newFuncp, argsp);
if (existingBodyp) newFuncp->addStmtsp(existingBodyp);
}
// IEEE: option / type_option members allow external access (cg_inst.option.X)
// and require std:: types; std:: is kept because setUsesStdPackage() is called
// at parse time for every covergroup declaration.
{
AstVar* const varp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
new AstClassOrPackageRef{nodep->fileline(), "std",
nullptr, nullptr},
nullptr}};
nodep->addMembersp(varp);
}
{
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}};
nodep->addMembersp(varp);
}
// IEEE: function void sample([arguments])
{
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
addArgMemberCopies(funcp, sampleArgsp);
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: function void start(), void stop()
for (const string& name : {"start"s, "stop"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: static function real get_coverage(optional ref int, optional ref int)
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
funcp->isStatic(name == "get_coverage");
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
{
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
nodep->findDoubleDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::OUTPUT);
varp->funcReturn(true);
funcp->fvarp(varp);
}
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
funcp->addStmtsp(varp);
}
}
// IEEE: function void set_inst_name(string)
{
AstFunc* const funcp
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
funcp->addStmtsp(varp);
}
}
void visit(AstCovergroup* nodep) override {
// AstCovergroup can only appear inside a module/class/package; never at root level.
UASSERT_OBJ(m_modp, nodep, "AstCovergroup not under module");
// If we're already inside a covergroup class, this is the sentinel AstCovergroup
// node carrying the clocking event for V3Covergroup - don't re-transform it.
if (VN_IS(m_modp, Class) && VN_AS(m_modp, Class)->isCovergroup()) return;
// Transform raw parse-time AstCovergroup into a fully-formed AstClass
cleanFileline(nodep);
const string libname = m_modp->libname();
AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname};
cgClassp->isCovergroup(true);
v3Global.useCovergroup(true);
// Clocking event: unlink before deleteTree, attach as AstCovergroup child on class
if (AstSenTree* const eventp = nodep->eventp()) {
eventp->unlinkFrBack();
AstCovergroup* const cgNodep = new AstCovergroup{
nodep->fileline(), nodep->name(), nullptr, nullptr, nullptr, eventp};
cgClassp->addMembersp(cgNodep);
}
// Convert constructor args to member variables
for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) {
AstVar* const origVarp = VN_AS(argp, Var);
AstVar* const memberp = origVarp->cloneTree(false);
memberp->varType(VVarType::MEMBER);
memberp->funcLocal(false);
memberp->direction(VDirection::NONE);
cgClassp->addMembersp(memberp);
}
// Convert sample args to member variables
for (AstNode* argp = nodep->sampleArgsp(); argp; argp = argp->nextp()) {
AstVar* const origVarp = VN_AS(argp, Var);
AstVar* const memberp = origVarp->cloneTree(false);
memberp->varType(VVarType::MEMBER);
memberp->funcLocal(false);
memberp->direction(VDirection::NONE);
cgClassp->addMembersp(memberp);
}
// Create the constructor; detach membersp (coverage body) and use as its body
AstFunc* const newFuncp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
newFuncp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
newFuncp->classMethod(true);
newFuncp->isConstructor(true);
newFuncp->dtypep(cgClassp->dtypep());
if (AstNode* const bodyp = nodep->membersp()) {
bodyp->unlinkFrBackWithNext();
newFuncp->addStmtsp(bodyp);
}
cgClassp->addMembersp(newFuncp);
// Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep)
createCovergroupMethods(cgClassp, newFuncp, nodep->argsp(), nodep->sampleArgsp());
// Replace AstCovergroup with AstClass and process the new class normally.
// Reset the unnamed-coverpoint counter so synthesized names are stable and
// independent of unrelated covergroups elsewhere in the file.
VL_RESTORER(m_coverpointNum);
m_coverpointNum = 0;
nodep->replaceWith(cgClassp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
iterate(cgClassp);
}
void visit(AstCoverpoint* nodep) override {
cleanFileline(nodep);
// Give every coverpoint a guaranteed-unique, deterministic name so all downstream
// consumers (generated bin-variable names, the cross coverpoint map, hierarchical
// report names) see a consistent identifier. Unlabeled coverpoints arrive from the
// grammar with an empty name; left empty, two of them in one covergroup collide on
// the generated "__Vcov__<bin>" variable name (e.g. duplicate "__Vcov__auto_0").
if (nodep->name().empty()) {
// A single-identifier coverpoint expression is the only form that parses to an
// AstParseRef with a usable name here; a dotted/scoped/select/concatenation/call
// expression is either a different node (so the cast yields null) or a name-less
// AstParseRef (e.g. a member-select). Either of those gets a synthesized name.
const AstParseRef* const refp = VN_CAST(nodep->exprp(), ParseRef);
if (refp && !refp->name().empty()) {
// Single-identifier coverpoint: take the variable's name (IEEE 1800-2023
// 19.5 - an unlabeled coverpoint of a single variable is named for it).
nodep->name(refp->name());
} else {
// Compound expression (member/part select, concatenation, ...): synthesize
// a unique name. Leading "__V" keeps it out of the user namespace.
nodep->name("__Vcoverpoint" + cvtToStr(m_coverpointNum++));
}
}
// Re-sort the parse-time mixed bins list (AstCoverBin + AstCgOptionAssign)
// into the typed binsp and optionsp slots. The grammar attaches both node types
// to binsp (op2) as a raw List[AstNode]; now that they are properly parented we
// can iterate and split them without any temporary-parent tricks.
for (AstNode *itemp = nodep->binsp(), *nextp; itemp; itemp = nextp) {
nextp = itemp->nextp();
if (AstCgOptionAssign* const optp = VN_CAST(itemp, CgOptionAssign)) {
optp->unlinkFrBack();
if (optp->optionType() == VCoverOptionType::AT_LEAST
|| optp->optionType() == VCoverOptionType::AUTO_BIN_MAX) {
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optionType(),
optp->valuep()->cloneTree(false)});
} else {
optp->v3warn(COVERIGN,
"Ignoring unsupported coverage option: " + optp->prettyNameQ());
}
VL_DO_DANGLING(optp->deleteTree(), optp);
}
}
iterateChildren(nodep);
}
void visit(AstCoverCross* nodep) override {
cleanFileline(nodep);
// Distribute the parse-time raw cross_body list (rawBodyp, op3) into the
// typed optionsp slot. The grammar produces AstCgOptionAssign nodes for
// option.* items; convert them to AstCoverOption exactly as visit(AstCoverpoint*)
// does. Other items (functions, unsupported bin selectors) are discarded.
for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) {
nextp = itemp->nextp();
itemp->unlinkFrBack();
AstCgOptionAssign* const optp = VN_AS(itemp, CgOptionAssign);
const VCoverOptionType optType = optp->optionType();
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)});
VL_DO_DANGLING(optp->deleteTree(), optp);
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
// Default: Just iterate
cleanFileline(nodep);

View File

@ -15,11 +15,13 @@
//*************************************************************************
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Control.h"
#include "V3Global.h"
#include "V3ParseImp.h" // Defines YYTYPE; before including bison header
#include <stack>
#include <vector>
class V3ParseGrammar final {
public:
@ -95,97 +97,6 @@ public:
nodep->trace(singletonp()->allTracingOn(fileline));
return nodep;
}
void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) {
// Hidden static to take unspecified reference argument results
AstVar* const defaultVarp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
nodep->addStmtsp(defaultVarp);
// IEEE: option
{
v3Global.setUsesStdPackage();
AstVar* const varp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
new AstClassOrPackageRef{nodep->fileline(), "std",
nullptr, nullptr},
nullptr}};
nodep->addMembersp(varp);
}
// IEEE: type_option
{
v3Global.setUsesStdPackage();
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}};
nodep->addMembersp(varp);
}
// IEEE: function void sample()
{
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
funcp->addStmtsp(sampleArgs);
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: function void start(), void stop()
for (const string& name : {"start"s, "stop"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: static function real get_coverage(optional ref int, optional ref int)
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
funcp->isStatic(name == "get_coverage");
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
{
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
nodep->findDoubleDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::OUTPUT);
varp->funcReturn(true);
funcp->fvarp(varp);
}
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
funcp->addStmtsp(varp);
}
}
// IEEE: function void set_inst_name(string)
{
AstFunc* const funcp
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
funcp->addStmtsp(varp);
}
}
AstDisplay* createDisplayError(FileLine* fileline) {
AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr};
AstNode::addNext<AstNode, AstNode>(nodep, new AstStop{fileline, false});

View File

@ -1355,6 +1355,7 @@ class TaskVisitor final : public VNVisitor {
cfuncp->isVirtual(nodep->isVirtual());
cfuncp->dpiPure(nodep->dpiPure());
if (nodep->name() == "new") cfuncp->isConstructor(true);
if (nodep->isCovergroupSample()) cfuncp->isCovergroupSample(true);
if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());

View File

@ -343,11 +343,15 @@ class TimingSuspendableVisitor final : public VNVisitor {
}
}
void visit(AstNodeCCall* nodep) override {
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()), getSuspendDepVtx(m_procp),
P_CALL};
AstCFunc* const funcp = nodep->funcp();
UASSERT_OBJ(funcp, nodep, "AstNodeCCall must have non-null funcp post-link");
UASSERT_OBJ(m_procp, nodep, "AstNodeCCall must be inside a procedure/CFunc/Begin");
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
getNeedsProcDepVtx(m_procp), P_CALL};
UINFO(9, "V3Timing: Processing CCall to " << funcp->name() << " in dependency graph\n");
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(funcp), getSuspendDepVtx(m_procp), P_CALL};
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(funcp), getNeedsProcDepVtx(m_procp),
P_CALL};
iterateChildren(nodep);
}
@ -976,8 +980,16 @@ class TimingControlVisitor final : public VNVisitor {
}
}
void visit(AstNodeCCall* nodep) override {
if (nodep->funcp()->needProcess()) m_hasProcess = true;
if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
AstCFunc* const funcp = nodep->funcp();
// Skip automatic covergroup sampling calls
if (funcp->isCovergroupSample()) {
iterateChildren(nodep);
return;
}
if (funcp->needProcess()) m_hasProcess = true;
if (hasFlags(funcp, T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
// Calls to suspendables are always void return type, hence parent must be StmtExpr
AstStmtExpr* const stmtp = VN_AS(nodep->backp(), StmtExpr);
stmtp->replaceWith(new AstCAwait{nodep->fileline(), nodep->unlinkFrBack()});

View File

@ -225,6 +225,7 @@ class WidthVisitor final : public VNVisitor {
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstClass* m_cgClassp = nullptr; // Current covergroup class
AstNodeModule* m_modep = nullptr; // Current module
const AstConstraint* m_constraintp = nullptr; // Current constraint
AstNodeProcedure* m_procedurep = nullptr; // Current final/always
@ -1896,10 +1897,35 @@ class WidthVisitor final : public VNVisitor {
if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
}
void visit(AstCgOptionAssign* nodep) override {
// We report COVERIGN on the whole covergroup; if get more fine-grained add this
// nodep->v3warn(COVERIGN, "Ignoring unsupported: coverage option");
// Extract covergroup option values and store in AstClass before deleting.
// m_cgClassp is always set here: AstCgOptionAssign only appears in covergroup
// class bodies, and visitClass sets m_cgClassp before iterating children.
if (nodep->optionType() == VCoverOptionType::AUTO_BIN_MAX) {
// By V3Width time, V3Param has already folded any parameter references.
// If the value is still not a constant, it is a runtime expression - emit error.
if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) {
m_cgClassp->cgAutoBinMax(constp->toSInt());
UINFO(6, " Covergroup " << m_cgClassp->name()
<< " option.auto_bin_max = " << constp->toSInt() << endl);
} else {
nodep->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant "
"'option.auto_bin_max'; using default value");
}
}
// Add more options here as needed (weight, goal, at_least, per_instance, comment)
// Delete the assignment node (we've extracted the value)
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstCoverpoint* nodep) override {
// The coverpoint expression is self-determined (IEEE 1800-2023 19.5). Width it
// with a context so a bit/part-select (AstSel) is sized here; otherwise it would
// reach assertAtExpr() with m_vup==null and fail as an internal error.
userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
userIterateAndNext(nodep->binsp(), nullptr);
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
userIterateAndNext(nodep->optionsp(), nullptr);
}
void visit(AstPow* nodep) override {
// Pow is special, output sign only depends on LHS sign, but
// function result depends on both signs
@ -3539,7 +3565,16 @@ class WidthVisitor final : public VNVisitor {
return AstEqWild::newTyped(itemp->fileline(), exprp, itemp->unlinkFrBack());
}
void visit(AstInsideRange* nodep) override {
// Just do each side; AstInside will rip these nodes out later
// Just do each side; AstInside will rip these nodes out later.
// When m_vup is null, this range appears outside a normal expression context (e.g.
// in a covergroup bin declaration). Pre-fold constant arithmetic in that case
// (e.g., AstNegate(Const) -> Const) so children have their types set before widthing.
// We cannot do this unconditionally: in a normal 'inside' expression (m_vup set),
// range bounds may be enum refs not yet widthed, and constifyEdit would crash.
if (!m_vup) {
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
}
userIterateAndNext(nodep->lhsp(), m_vup);
userIterateAndNext(nodep->rhsp(), m_vup);
nodep->dtypeFrom(nodep->lhsp());
@ -7712,6 +7747,8 @@ class WidthVisitor final : public VNVisitor {
// Must do extends first, as we may in functions under this class
// start following a tree of extends that takes us to other classes
userIterateAndNext(nodep->extendsp(), nullptr);
VL_RESTORER(m_cgClassp);
if (nodep->isCovergroup()) m_cgClassp = nodep;
userIterateChildren(nodep, nullptr); // First size all members
}
void visit(AstNodeModule* nodep) override {

View File

@ -38,6 +38,7 @@
#include "V3Control.h"
#include "V3Coverage.h"
#include "V3CoverageJoin.h"
#include "V3Covergroup.h"
#include "V3Dead.h"
#include "V3Delayed.h"
#include "V3Depth.h"
@ -240,6 +241,10 @@ static void process() {
// the AST context needed to recover and lower FSMs reliably.
if (v3Global.opt.coverageNonFsm()) V3Coverage::coverage(v3Global.rootp());
// Functional coverage code generation
// Generate code for covergroups/coverpoints
if (v3Global.useCovergroup()) V3Covergroup::covergroup(v3Global.rootp());
// Resolve randsequence if they are used by the design
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());

View File

@ -66,6 +66,11 @@ public:
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
string page() const { return keyExtract("page", m_name.c_str()); }
string type() const { return typeExtract(m_name.c_str()); }
// Covergroup-specific key accessors (long keys, no short-key alias)
string bin() const { return keyExtract("bin", m_name.c_str()); }
string binType() const { return keyExtract("bin_type", m_name.c_str()); }
bool isCross() const { return !keyExtract("cross", m_name.c_str()).empty(); }
string crossBins() const { return keyExtract(VL_CIK_CROSS_BINS, m_name.c_str()); }
string thresh() const {
// string as maybe ""
return keyExtract(VL_CIK_THRESH, m_name.c_str());
@ -123,6 +128,23 @@ public:
os << std::setw(6) << std::setfill('0') << count();
os << " point: type=" << type() << " comment=" << comment() << " hier=" << hier();
os << "\n";
if (isCross()) {
const string bins = crossBins();
if (!bins.empty()) {
os << " // cross: [";
bool first = true;
size_t start = 0;
while (true) {
const size_t comma = bins.find(',', start);
if (!first) os << ", ";
os << bins.substr(start, comma == string::npos ? string::npos : comma - start);
first = false;
if (comma == string::npos) break;
start = comma + 1;
}
os << "]\n";
}
}
}
};

View File

@ -25,8 +25,10 @@
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
@ -346,8 +348,6 @@ void VlcTop::rank() {
}
}
//######################################################################
void VlcTop::annotateCalc() {
// Calculate per-line information into filedata structure
for (const auto& i : m_points) {

View File

@ -3958,16 +3958,16 @@ value_range<nodeExprp>: // ==IEEE: value_range/open_value_range
covergroup_value_range<nodeExprp>: // ==IEEE-2012: covergroup_value_range
cgexpr { $$ = $1; }
| '[' cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
// // IEEE-2023: added all four:
// // Skipped as '$' is part of our expr
// // IEEE-2023: '[' '$' ':' cgexpr ']'
// // Skipped as '$' is part of our expr
// // IEEE-2023: '[' cgexpr ':' '$' ']'
| '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
| '[' cgexpr yP_PLUSPCTMINUS cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
;
caseCondList<nodeExprp>: // IEEE: part of case_item
@ -6970,40 +6970,27 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
yCOVERGROUP idAny cgPortListE coverage_eventE ';'
/*cont*/ coverage_spec_or_optionListE
/*cont*/ yENDGROUP endLabelE
{ AstClass *cgClassp = new AstClass{$<fl>2, *$2, PARSEP->libname()};
cgClassp->isCovergroup(true);
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
newp->classMethod(true);
newp->isConstructor(true);
newp->dtypep(cgClassp->dtypep());
newp->addStmtsp($3);
newp->addStmtsp($6);
cgClassp->addMembersp(newp);
GRAMMARP->createCoverGroupMethods(cgClassp, $4);
$$ = cgClassp;
GRAMMARP->endLabel($<fl>8, $$, $8);
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
}
{ AstSenTree* clockp = nullptr;
AstNode* sampleArgsp = nullptr;
if ($4) {
if (VN_IS($4, SenItem)) {
clockp = new AstSenTree{$<fl>1, VN_AS($4, SenItem)};
} else {
sampleArgsp = $4;
}
}
$$ = new AstCovergroup{$<fl>1, *$2, static_cast<AstVar*>($3),
static_cast<AstVar*>(sampleArgsp), $6, clockp};
// Every covergroup has option/type_option members (added by V3LinkParse)
// referencing std:: types, so mark std as needed at parse time.
v3Global.setUsesStdPackage();
GRAMMARP->endLabel($<fl>8, $$, $8); }
| yCOVERGROUP yEXTENDS idAny ';'
/*cont*/ coverage_spec_or_optionListE
/*cont*/ yENDGROUP endLabelE
{ AstClass *cgClassp = new AstClass{$<fl>3, *$3, PARSEP->libname()};
cgClassp->isCovergroup(true);
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
newp->classMethod(true);
newp->isConstructor(true);
newp->dtypep(cgClassp->dtypep());
newp->addStmtsp($5);
cgClassp->addMembersp(newp);
GRAMMARP->createCoverGroupMethods(cgClassp, nullptr);
$$ = cgClassp;
GRAMMARP->endLabel($<fl>7, $$, $7);
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
}
{ $$ = nullptr;
BBUNSUP($1, "Unsupported: covergroup inheritance (extends)");
DEL($5); }
;
cgPortListE<nodep>:
@ -7036,12 +7023,61 @@ coverage_spec_or_option<nodep>: // ==IEEE: coverage_spec_or_option
coverage_option<nodep>: // ==IEEE: coverage_option
// // option/type_option aren't really keywords
id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr
{ if (*$1 == "option") {
$$ = new AstCgOptionAssign{$<fl>1, false, *$3, $5};
} else if (*$1 == "type_option") {
$$ = new AstCgOptionAssign{$<fl>1, true, *$3, $5};
{ $$ = nullptr;
if (*$1 == "option" || *$1 == "type_option") {
const bool typeOpt = (*$1 == "type_option");
VCoverOptionType optType = VCoverOptionType::UNKNOWN;
bool valid = true;
if (!typeOpt) {
// IEEE 1800-2023 Table 19-1: option.* names
if (*$3 == "at_least") optType = VCoverOptionType::AT_LEAST;
else if (*$3 == "auto_bin_max") optType = VCoverOptionType::AUTO_BIN_MAX;
else if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
else if (*$3 == "cross_num_print_missing") optType = VCoverOptionType::CROSS_NUM_PRINT_MISSING;
else if (*$3 == "cross_retain_auto_bins") optType = VCoverOptionType::CROSS_RETAIN_AUTO_BINS;
else if (*$3 == "detect_overlap") optType = VCoverOptionType::DETECT_OVERLAP;
else if (*$3 == "get_inst_coverage") optType = VCoverOptionType::GET_INST_COVERAGE;
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
else if (*$3 == "name") optType = VCoverOptionType::NAME;
else if (*$3 == "per_instance") optType = VCoverOptionType::PER_INSTANCE;
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
else {
$<fl>1->v3error("Unknown coverage option name 'option."
<< *$3 << "'"
<< "; not a valid option per IEEE 1800-2023 Table 19-1");
valid = false;
}
} else {
// IEEE 1800-2023 Table 19-3: type_option.* names
if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
else if (*$3 == "distribute_first") optType = VCoverOptionType::DISTRIBUTE_FIRST;
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
else if (*$3 == "merge_instances") optType = VCoverOptionType::MERGE_INSTANCES;
else if (*$3 == "real_interval") optType = VCoverOptionType::REAL_INTERVAL;
else if (*$3 == "strobe") optType = VCoverOptionType::STROBE;
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
else {
// Specific message for option.* names used under type_option.*
if (*$3 == "at_least" || *$3 == "auto_bin_max"
|| *$3 == "cross_num_print_missing" || *$3 == "cross_retain_auto_bins"
|| *$3 == "detect_overlap" || *$3 == "get_inst_coverage"
|| *$3 == "name" || *$3 == "per_instance") {
$<fl>1->v3error("'type_option." << *$3
<< "' is not valid; use 'option." << *$3 << "' instead");
} else {
$<fl>1->v3error("Unknown coverage type option name 'type_option."
<< *$3 << "'"
<< "; not a valid type option per IEEE 1800-2023 Table 19-3");
}
valid = false;
}
}
if (valid) {
$$ = new AstCgOptionAssign{$<fl>1, typeOpt, optType, *$3, $5};
} else {
DEL($5);
}
} else {
$$ = nullptr;
$<fl>1->v3error("Syntax error; expected 'option' or 'type_option': '" << *$1 << "'");
DEL($5);
} }
@ -7050,29 +7086,33 @@ coverage_option<nodep>: // ==IEEE: coverage_option
cover_point<nodep>: // ==IEEE: cover_point
// // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT
yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverpoint"); DEL($2, $3, $4); }
{ $$ = new AstCoverpoint{$<fl>1, "", $2, $3, $4}; }
// // IEEE-2012: class_scope before an ID
| id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: coverpoint"); DEL($4, $5, $6);}
{ $$ = new AstCoverpoint{$<fl>3, *$1, $4, $5, $6}; }
// // data_type_or_implicit expansion
| data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($1, $5, $6, $7);}
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7};
DEL($1); }
| yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); }
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7}; }
// // IEEE-2012:
| bins_or_empty { $$ = $1; }
;
iffE<nodep>: // IEEE: part of cover_point, others
iffE<nodeExprp>: // IEEE: part of cover_point, others
/* empty */ { $$ = nullptr; }
| yIFF '(' expr ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover 'iff'"); DEL($3); }
{ $$ = $3; /* Keep iff condition for coverpoint */ }
;
bins_or_empty<nodep>: // ==IEEE: bins_or_empty
@ -7096,39 +7136,104 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
// // Superset of IEEE - we allow []'s in more places
coverage_option { $$ = $1; }
// // Can't use wildcardE as results in conflicts
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin specification"); DEL($3, $6, $8); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>8, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $6, $10, $12); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $8, $10); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'wildcard' specification"); DEL($4, $7, $9); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($4, $7, $11, $13); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
if ($3) binp->isArray(true);
$$ = binp; DEL($8); }
| yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE
{ // Check for automatic bins: bins auto[N]
if (*$2 == "auto") {
$$ = new AstCoverBin{$<fl>2, *$2, $4};
DEL($6);
} else {
$$ = nullptr;
BBCOVERIGN($<fl>2, "Unsupported: 'bins' array (non-auto)");
DEL($4, $6);
}
}
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
if ($3) binp->isArray(true);
$$ = binp; DEL($8); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
if ($3) binp->isArray(true);
$$ = binp; DEL($8); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
DEL($10, $12); $$ = binp; }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true};
DEL($9); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true};
DEL($9); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true};
DEL($9); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
//
// // cgexpr part of trans_list
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin trans list"); DEL($3, $5, $6); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($4, $6, $7);}
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_TRANSITION}, isArray != nullptr};
DEL($6); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_IGNORE}, isArray != nullptr};
DEL($6); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_ILLEGAL}, isArray != nullptr};
DEL($6); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
//
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'default'"); DEL($3, $6); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($3, $7); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_DEFAULT};
DEL($6); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_IGNORE};
DEL($6); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_ILLEGAL};
DEL($6); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
;
bins_orBraE<nodep>: // IEEE: part of bins_or_options:
bins_orBraE<fl>: // IEEE: part of bins_or_options: returns fileline (abuse for boolean flag)
/* empty */ { $$ = nullptr; }
| '[' ']' { $$ = nullptr; /*UNSUP*/ }
| '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); }
;
bins_keyword<fl>: // ==IEEE: bins_keyword
yBINS { $$ = $1; /*UNSUP*/ }
| yILLEGAL_BINS { $$ = $1; /*UNSUP*/ }
| yIGNORE_BINS { $$ = $1; /*UNSUP*/ }
| '[' ']' { $$ = $<fl>1; /* Mark as array */ }
| '[' cgexpr ']' { BBCOVERIGN($<fl>1, "Unsupported: 'bins' explicit array size (treated as '[]')"); DEL($2); $$ = $<fl>1; }
;
trans_list<nodep>: // ==IEEE: trans_list
@ -7136,30 +7241,42 @@ trans_list<nodep>: // ==IEEE: trans_list
| trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); }
;
trans_set<nodep>: // ==IEEE: trans_set
trans_range_list { $$ = $1; }
// // Note the { => } in the grammar, this is really a list
trans_set<nodep>: // ==IEEE: trans_set (returns AstCoverTransSet)
trans_range_list {
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($1);
$$ = new AstCoverTransSet{$<fl>1, itemp};
}
| trans_set yP_EQGT trans_range_list
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); }
{
AstCoverTransSet* const setp = static_cast<AstCoverTransSet*>($1);
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($3);
setp->addItemsp(itemp);
$$ = setp;
}
;
trans_range_list<nodep>: // ==IEEE: trans_range_list
trans_item { $$ = $1; }
trans_range_list<nodep>: // ==IEEE: trans_range_list (returns AstCoverTransItem)
trans_item {
// Simple transition item without repetition
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONE};
}
| trans_item yP_BRASTAR cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3); }
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3, $5); }
| trans_item yP_BRAMINUSGT cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3); }
{ BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($3);
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::GOTO}; }
| trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($1, $3, $5); }
| trans_item yP_BRAEQ cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3); }
{ BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($3);
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONCONS}; }
| trans_item yP_BRAEQ cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($1, $3, $5); }
;
trans_item<nodep>: // ==IEEE: range_list
trans_item<nodep>: // ==IEEE: range_list (returns range list node)
covergroup_range_list { $$ = $1; }
;
@ -7171,9 +7288,28 @@ covergroup_range_list<nodep>: // ==IEEE: covergroup_range_list
cover_cross<nodep>: // ==IEEE: cover_cross
id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: cover cross"); DEL($4, $5, $6); }
{
AstCoverCross* const nodep = new AstCoverCross{$<fl>3, *$1,
VN_AS($4, CoverpointRef)};
if ($6) nodep->addRawBodyp($6);
if ($5) {
$5->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
VL_DO_DANGLING($5->deleteTree(), $5);
}
$$ = nodep;
}
| yCROSS list_of_cross_items iffE cross_body
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover cross"); DEL($2, $3, $4); }
{
AstCoverCross* const nodep = new AstCoverCross{$<fl>1,
"__cross" + cvtToStr(GRAMMARP->s_typeImpNum++),
VN_AS($2, CoverpointRef)};
if ($4) nodep->addRawBodyp($4);
if ($3) {
$3->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
VL_DO_DANGLING($3->deleteTree(), $3);
}
$$ = nodep;
}
;
list_of_cross_items<nodep>: // ==IEEE: list_of_cross_items
@ -7188,7 +7324,8 @@ cross_itemList<nodep>: // IEEE: part of list_of_cross_items
;
cross_item<nodep>: // ==IEEE: cross_item
idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ }
id/*cover_point_identifier*/
{ $$ = new AstCoverpointRef{$<fl>1, *$1}; }
;
cross_body<nodep>: // ==IEEE: cross_body
@ -7208,12 +7345,16 @@ cross_body_itemList<nodep>: // IEEE: part of cross_body
cross_body_item<nodep>: // ==IEEE: cross_body_item
function_declaration
{ $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); }
{ $$ = nullptr; BBCOVERIGN($1->fileline(), "Unsupported: 'function' in coverage cross body"); DEL($1); }
// // IEEE: bins_selection_or_option
| coverage_option ';' { $$ = $1; }
// // IEEE: bins_selection
| bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); DEL($4, $5); }
// // IEEE: bins_selection - for now, we ignore explicit cross bins
| yBINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
| yIGNORE_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
| yILLEGAL_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
| error ';' { $$ = nullptr; } // LCOV_EXCL_LINE
;
@ -7221,28 +7362,28 @@ select_expression<nodep>: // ==IEEE: select_expression
select_expression_r
{ $$ = $1; }
| select_expression yP_ANDAND select_expression
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); }
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '&&' in coverage select expression"); DEL($1, $3); }
| select_expression yP_OROR select_expression
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); }
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '||' in coverage select expression"); DEL($1, $3); }
;
// This non-terminal exists to disambiguate select_expression and make "with" bind tighter
select_expression_r<nodep>:
// // IEEE: select_condition expanded here
yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($3); }
| '!' yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($4); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($4); }
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($3, $7); }
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($7); }
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($4, $8); }
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($4, $8); }
| yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($3); }
| '!' yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($4); }
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($4); }
| select_expression_r yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression with"); DEL($1, $4); }
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: 'with' in coverage select expression"); DEL($1, $4); }
// // IEEE-2012: Need clarification as to precedence
//UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { }
// // IEEE-2012: Need clarification as to precedence
@ -7260,7 +7401,7 @@ select_expression_r<nodep>:
//UNSUP cgexpr yMATCHES cgexpr {..}
//UNSUP // Below are all removed
| idAny '(' list_of_argumentsE ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage select function call"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: function call in coverage select expression"); DEL($3); }
//UNSUP // Above are all removed, replace with:
;
@ -7279,7 +7420,7 @@ bins_expression<nodep>: // ==IEEE: bins_expression
coverage_eventE<nodep>: // IEEE: [ coverage_event ]
/* empty */ { $$ = nullptr; }
| clocking_event
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage clocking event"); DEL($1); }
{ $$ = $1; } // Keep the clocking event for automatic sampling
| yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')'
{ if (*$3 != "sample") {
$<fl>3->v3error("Coverage sampling function must be named 'sample'");
@ -7290,7 +7431,7 @@ coverage_eventE<nodep>: // IEEE: [ coverage_event ]
}
}
| yP_ATAT '(' block_event_expression ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage '@@' events"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: '@@' coverage event"); DEL($3); }
;
block_event_expression<nodep>: // ==IEEE: block_event_expression
@ -7788,11 +7929,15 @@ class_item<nodep>: // ==IEEE: class_item
| timeunits_declaration { $$ = $1; }
| covergroup_declaration
{
const string cgName = $1->name();
$1->name("__vlAnonCG_" + cgName);
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
$$ = addNextNull($1, newp);
if ($1) {
const string cgName = $1->name();
$1->name("__vlAnonCG_" + cgName);
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
$$ = addNextNull($1, newp);
} else {
$$ = nullptr;
}
}
// // local_parameter_declaration under parameter_declaration
| parameter_declaration ';' { $$ = $1; }

View File

@ -0,0 +1,53 @@
# 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 re
def covergroup_coverage_report(test, outfile=None):
"""Parse coverage.dat and write a sorted covergroup bin hit-count report.
Lines have the form: <hierarchy>[ [bin_type]]: <count>
ignore_bins and illegal_bins are annotated with [ignore] / [illegal].
Returns the path to the written report file.
"""
if outfile is None:
outfile = test.obj_dir + "/covergroup_report.txt"
contents = test.file_contents(test.coverage_filename)
entries = []
for m in re.finditer(r"C '([^']+)' (\d+)", contents):
entry, count = m.group(1), m.group(2)
if '\x01t\x02covergroup' not in entry:
continue
h_m = re.search(r'\x01h\x02([^\x01]+)', entry)
if not h_m:
continue
hier = h_m.group(1)
bt_m = re.search(r'\x01bin_type\x02([^\x01]+)', entry)
cross_m = re.search(r'\x01cross\x021', entry)
annotations = []
if bt_m:
annotations.append(bt_m.group(1))
if cross_m:
annotations.append("cross")
label = f"{hier} [{','.join(annotations)}]" if annotations else hier
entries.append((hier, label, int(count)))
entries.sort()
with open(outfile, 'w', encoding='utf-8') as fh:
for _hier, label, count in entries:
fh.write(f"{label}: {count}\n")
return outfile
def run(test, *, verilator_flags2=()):
test.compile(verilator_flags2=['--coverage', *verilator_flags2])
test.execute()
covergroup_coverage_report(test)
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,6 @@
cg.cp1.hi: 0
cg.cp1.lo: 1
cg_clocked.cp_clocked.hi: 0
cg_clocked.cp_clocked.lo: 1
cg_samp.cp.b0: 1
cg_samp.cp.b3: 1

View File

@ -9,8 +9,8 @@
import vltest_bootstrap
import coverage_covergroup_common
test.scenarios('vlt')
test.compile()
test.passes()
coverage_covergroup_common.run(test)

View File

@ -4,35 +4,85 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilator lint_off COVERIGN
// A plain (non-covergroup) class included to verify it does not interfere with covergroup handling
class PlainClass;
int x;
endclass
// Top-level (file-scope) covergroup declared outside any module
covergroup cg_toplevel;
cp_tl: coverpoint 0;
endgroup
module t;
int i, j;
logic clk = 0;
covergroup cg(int var1, int var2 = 42);
cp1: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; }
endgroup
// Clocked covergroup with constructor arguments
covergroup cg_clocked(int lim) @(posedge clk);
cp_clocked: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; }
endgroup
// 'with function sample' covergroup whose coverpoint references its own sample-argument
// member. That reference resolves to a member of the covergroup class itself and so must
// NOT be mistaken for an unsupported enclosing-class reference (and skipped).
covergroup cg_samp with function sample(bit [1:0] x);
cp: coverpoint x { bins b0 = {0}; bins b3 = {3}; }
endgroup
cg cov1 = new(69, 77);
cg cov2 = new(69);
int i, j;
real r;
cg_clocked cov_clocked = new(10);
cg_samp cov_samp = new;
PlainClass plain_inst = new; // Non-covergroup class instance - must not affect covergroup coverage
function void x();
real cov_result;
cov1.set_inst_name("the_inst_name");
cov1.start();
cov1.sample();
cov1.stop();
void'(cov2.get_coverage());
r = cov2.get_coverage();
r = cov2.get_coverage(i, j);
// verilator lint_off IGNOREDRETURN
cov2.get_inst_coverage();
// verilator lint_on IGNOREDRETURN
r = cov2.get_inst_coverage(i, j);
cov_result = cov2.get_coverage();
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_coverage() out of range: %f", cov_result);
cg::get_coverage();
r = cg::get_coverage();
r = cg::get_coverage(i, j);
cov_result = cov2.get_coverage(i, j);
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_coverage(i,j) return out of range: %f", cov_result);
cov_result = cov2.get_inst_coverage();
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_inst_coverage() out of range: %f", cov_result);
cov_result = cov2.get_inst_coverage(i, j);
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_inst_coverage(i,j) return out of range: %f", cov_result);
cov_result = cg::get_coverage();
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: cg::get_coverage() out of range: %f", cov_result);
cov_result = cg::get_coverage(i, j);
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: cg::get_coverage(i,j) return out of range: %f", cov_result);
endfunction
initial begin
i = 3;
x(); // samples cov1 with i=3 -> lo bin hit
clk = 1; // posedge: samples cov_clocked with i=3 -> lo bin hit
// Sample-arg coverpoint: the passed value must reach the coverpoint. Sampling 0 then 3
// must hit b0 and b3 respectively; if the argument were dropped (member left at its
// default 0) b3 would never be hit.
cov_samp.sample(2'd0);
cov_samp.sample(2'd3);
$finish;
end
endmodule

View File

@ -0,0 +1,4 @@
cg.data.grouped: 2
cg.data.values: 3
cg2.cp.range_arr: 3
cg3.cp.range_sized: 3

View File

@ -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')
coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN'])

View File

@ -0,0 +1,94 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test array bins - separate bin per value, including range expressions
// 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 [7:0] data;
covergroup cg;
coverpoint data {
// Array bins: creates 3 separate bins
bins values[] = {1, 5, 9};
// Non-array bin: creates 1 bin covering all values
bins grouped = {2, 6, 10};
}
endgroup
// cg2: array bins using a range expression - one bin per value in the range
covergroup cg2;
cp: coverpoint data {
bins range_arr[] = {[0:3]}; // range expression: creates 4 separate bins
}
endgroup
// cg3: sized array bins - bins r[N] = {[lo:hi]} distributes range into N bins
covergroup cg3;
cp: coverpoint data {
bins range_sized[4] = {[4:7]}; // explicit count: 4 bins covering [4:7]
}
endgroup
initial begin
cg cg_inst;
cg2 cg2_inst;
cg3 cg3_inst;
cg_inst = new();
cg2_inst = new();
cg3_inst = new();
// Hit first array bin value (1)
data = 1;
cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 25.0);
// Hit second array bin value (5)
data = 5;
cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 50.0);
// Hit the grouped bin (covers all of 2, 6, 10)
data = 6;
cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 75.0);
// Hit third array bin value (9)
data = 9;
cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0);
// Verify hitting other values in grouped bin doesn't increase coverage
data = 2;
cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0);
// Hit range_arr bins ([0:3])
data = 0; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 25.0);
data = 1; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 50.0);
data = 2; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 75.0);
// Hit range_sized bins ([4:7])
data = 4; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 25.0);
data = 5; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 50.0);
data = 6; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 75.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,27 @@
cg1.cp_data3.auto_0: 1
cg1.cp_data3.auto_1: 0
cg1.cp_data3.auto_2: 0
cg1.cp_data3.auto_3: 1
cg1.cp_data3.auto_4: 0
cg1.cp_data3.auto_5: 0
cg1.cp_data3.auto_6: 0
cg1.cp_data3.auto_7: 0
cg2.cp_data3.auto_0: 1
cg2.cp_data3.auto_1: 0
cg2.cp_data3.auto_2: 1
cg2.cp_data3.auto_3: 0
cg3.cp_data3.auto_0: 1
cg3.cp_data3.auto_1: 1
cg4.cp.auto_0: 0
cg4.cp.auto_1: 1
cg4.cp.auto_2: 1
cg4.cp.auto_3: 1
cg4.cp.ign [ignore]: 0
cg5.cp_data64.auto_0: 1
cg5.cp_data64.auto_1: 1
cg5.cp_data64.auto_2: 1
cg5.cp_data64.auto_3: 1
cg6.cp_data3.auto_0: 1
cg6.cp_data3.auto_1: 0
cg6.cp_data3.auto_2: 0
cg6.cp_data3.auto_3: 1

View File

@ -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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
import coverage_covergroup_common
test.scenarios('vlt')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,115 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-FileCopyrightText: 2024 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test implicit auto-bin creation (no explicit bins) and option.auto_bin_max
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [2:0] data3;
logic [3:0] data4;
logic [63:0] data64; // 64-bit signal
// Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal
covergroup cg1;
cp_data3: coverpoint data3;
endgroup
// Test 2: auto_bin_max = 4 at covergroup level - creates 4 bins: [0:1],[2:3],[4:5],[6:7]
covergroup cg2;
option.auto_bin_max = 4;
cp_data3: coverpoint data3;
endgroup
// Test 3: auto_bin_max and at_least set at coverpoint level
covergroup cg3;
cp_data3: coverpoint data3 {
option.auto_bin_max = 2; // coverpoint-level: creates 2 bins [0:3],[4:7]
option.at_least = 3; // coverpoint-level at_least
}
endgroup
// Test 4: auto-bins where all values in a range are excluded by ignore_bins
// auto_bin_max=4 on 4-bit signal -> 4 range bins: [0:3],[4:7],[8:11],[12:15].
// ignore_bins {[0:3]} excludes all values in the first range -> that bin is skipped.
covergroup cg4;
option.auto_bin_max = 4;
cp: coverpoint data4 {
ignore_bins ign = {[0:3]}; // first range excluded from coverage
}
endgroup
// Test 5: auto-bins on a 64-bit coverpoint with auto_bin_max=4
covergroup cg5;
option.auto_bin_max = 4;
cp_data64: coverpoint data64;
endgroup
// Test option.auto_bin_max at covergroup level: creates 4 bins [0:1],[2:3],[4:5],[6:7]
covergroup cg6;
option.auto_bin_max = 4;
cp_data3: coverpoint data3;
endgroup
initial begin
cg1 cg1_inst;
cg2 cg2_inst;
cg3 cg3_inst;
cg4 cg4_inst;
cg5 cg5_inst;
cg6 cg6_inst;
cg1_inst = new;
cg2_inst = new;
cg3_inst = new;
cg4_inst = new;
cg5_inst = new;
cg6_inst = new;
data3 = 0; cg1_inst.sample();
`checkr(cg1_inst.get_inst_coverage(), 12.5); // 1/8 bins hit
data3 = 3; cg1_inst.sample();
`checkr(cg1_inst.get_inst_coverage(), 25.0); // 2/8 bins hit
data3 = 0; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1]
data3 = 4; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[4:5]
// cg3: at_least=3 at coverpoint level; both samples have count=1 < 3 -> 0% throughout
data3 = 1; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 0.0);
data3 = 5; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 0.0);
// Sample valid (non-ignored) values for cg4
// cg4: auto_bin_max=4 creates 4 bins [0:3],[4:7],[8:11],[12:15].
// ignore_bins ign={[0:3]} excludes [0:3] values; Verilator keeps all 4 bins in denominator.
// 3 of 4 bins hit -> 75% (the [0:3] bin is included in denominator but can never be hit)
data4 = 4; cg4_inst.sample(); // [4:7] bin
data4 = 8; cg4_inst.sample(); // [8:11] bin
data4 = 12; cg4_inst.sample(); // [12:15] bin
`checkr(cg4_inst.get_inst_coverage(), 75.0);
// Sample cg5: 64-bit coverpoint - SKIP: Verilator 64-bit bin boundary bug causes 100% at first sample
data64 = 64'h0; cg5_inst.sample();
data64 = 64'h1111111111111111; cg5_inst.sample();
data64 = 64'hffffffffffffffff; cg5_inst.sample();
data3 = 0; cg6_inst.sample();
`checkr(cg6_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1]
data3 = 7; cg6_inst.sample();
`checkr(cg6_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[6:7]
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,7 @@
%Warning-COVERIGN: t/t_covergroup_auto_bin_max_bad.v:14:29: Ignoring unsupported: non-constant 'option.auto_bin_max'; using default value
: ... note: In instance 't'
14 | option.auto_bin_max = size_var;
| ^~~~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This file ONLY is placed under the Creative Commons Public Domain, for
# any use, without warranty, 2025 by Wilson Snyder.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t;
int size_var;
logic [3:0] cp_expr;
// Error: option.auto_bin_max must be a constant expression (group level)
covergroup cg;
option.auto_bin_max = size_var;
cp: coverpoint cp_expr;
endgroup
cg cg_i = new;
initial $finish;
endmodule

View File

@ -0,0 +1,15 @@
cg.data.auto[0]: 1
cg.data.auto[1]: 1
cg.data.auto[2]: 1
cg.data.auto[3]: 1
cg2.data64.auto_0: 1
cg2.data64.auto_1: 1
cg_4bit.data4.auto[0]: 1
cg_4bit.data4.auto[1]: 1
cg_4bit.data4.auto[2]: 1
cg_4bit.data4.auto[3]: 1
cg_4bit_excl.data4.auto[0]: 1
cg_4bit_excl.data4.auto[1]: 0
cg_4bit_excl.data4.auto[2]: 1
cg_4bit_excl.data4.auto[3]: 0
cg_4bit_excl.data4.bad [ignore]: 0

View File

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

View File

@ -0,0 +1,86 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Test automatic bins: bins auto[N]
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [2:0] data; // 3-bit: 0-7
logic [3:0] data4; // 4-bit signal
logic [63:0] data64; // 64-bit signal
covergroup cg;
coverpoint data {
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
}
endgroup
// 4-bit signal with auto[4]: creates 4 equal-width bins covering [0:15]
covergroup cg_4bit;
coverpoint data4 {
bins auto[4]; // Creates 4 bins: [0:3], [4:7], [8:11], [12:15]
}
endgroup
// 4-bit auto bins with one value excluded by ignore_bins
covergroup cg_4bit_excl;
coverpoint data4 {
ignore_bins bad = {0}; // value 0 excluded from auto expansion
bins auto[4];
}
endgroup
// 64-bit signal with auto_bin_max=2: creates 2 bins covering the full 64-bit range
covergroup cg2;
option.auto_bin_max = 2;
coverpoint data64;
endgroup
initial begin
automatic cg cg_inst = new;
automatic cg_4bit cg4_inst = new;
automatic cg_4bit_excl cg4e_inst = new;
automatic cg2 cg2_inst = new;
// Sample 3-bit cg: one value per bin - 4 bins: [0:1],[2:3],[4:5],[6:7]
data = 0; cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 25.0);
data = 2; cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 50.0);
data = 5; cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 75.0);
data = 7; cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0);
// Sample 4-bit bins - 4 bins: [0:3],[4:7],[8:11],[12:15]
data4 = 0; cg4_inst.sample(); // bin [0:3]
`checkr(cg4_inst.get_inst_coverage(), 25.0);
data4 = 7; cg4_inst.sample(); // bin [4:7]
`checkr(cg4_inst.get_inst_coverage(), 50.0);
data4 = 10; cg4_inst.sample(); // bin [8:11]
`checkr(cg4_inst.get_inst_coverage(), 75.0);
data4 = 14; cg4_inst.sample(); // bin [12:15]
`checkr(cg4_inst.get_inst_coverage(), 100.0);
// Sample 4-bit with exclusion (value 0 excluded; 4 auto bins for remaining values)
data4 = 1; cg4e_inst.sample();
`checkr(cg4e_inst.get_inst_coverage(), 25.0);
data4 = 8; cg4e_inst.sample();
`checkr(cg4e_inst.get_inst_coverage(), 50.0);
// Sample 64-bit cg2 - SKIP checkr: Verilator 64-bit bin boundary bug causes 100% at first sample
data64 = 64'd0; cg2_inst.sample();
data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,4 @@
cg.cp_data.one: 1
cg.cp_data.three: 1
cg.cp_data.two: 1
cg.cp_data.zero: 2

View File

@ -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
import coverage_covergroup_common
test.scenarios('vlt')
# Use the same .v file as the non-timing test
test.top_filename = "t/t_covergroup_clocked_sample.v"
coverage_covergroup_common.run(test, verilator_flags2=['--timing'])

View File

@ -0,0 +1,76 @@
%Error: t/t_covergroup_autobins_bad.v:17:12: Automatic bins array size must be a constant
: ... note: In instance 't'
17 | bins auto[size_var];
| ^~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_covergroup_autobins_bad.v:24:12: Automatic bins array size must be >= 1, got 0
: ... note: In instance 't'
24 | bins auto[0];
| ^~~~
%Error: t/t_covergroup_autobins_bad.v:31:12: Automatic bins array size of 1001 exceeds limit of 1000
: ... note: In instance 't'
31 | bins auto[1001];
| ^~~~
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin value list; values must be constants
: ... note: In instance 't'
43 | ignore_bins ign = {size_var};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:44:32: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
44 | ignore_bins ign_range = {[0:size_var]};
| ^
%Error: t/t_covergroup_autobins_bad.v:38:12: Non-constant expression in array bins range; range bounds must be constants
: ... note: In instance 't'
38 | bins b[] = {[size_var:size_var]};
| ^
%Error: t/t_covergroup_autobins_bad.v:39:12: Non-constant expression in array bins range; range bounds must be constants
: ... note: In instance 't'
39 | bins b_mixed[] = {[0:size_var]};
| ^~~~~~~
%Error: t/t_covergroup_autobins_bad.v:40:23: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
40 | bins b_range = {[size_var:4]};
| ^
%Error: t/t_covergroup_autobins_bad.v:41:24: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
41 | bins b_range2 = {[0:size_var]};
| ^
%Error: t/t_covergroup_autobins_bad.v:42:18: Non-constant expression in bin range; values must be constants
: ... note: In instance 't'
42 | bins b2 = {size_var};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin range; values must be constants
: ... note: In instance 't'
43 | ignore_bins ign = {size_var};
| ^~~~~~~~
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:51:25: Ignoring unsupported: non-constant 'option.at_least'; using default value
: ... note: In instance 't'
51 | option.at_least = size_var;
| ^~~~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: t/t_covergroup_autobins_bad.v:61:31: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
61 | ignore_bins ign_nclo = {[size_var:4]};
| ^
%Error: t/t_covergroup_autobins_bad.v:58:20: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
: ... note: In instance 't'
58 | bins b_xz = {[4'bxxxx:4'hF]};
| ^
%Error: t/t_covergroup_autobins_bad.v:59:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
: ... note: In instance 't'
59 | ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]};
| ^
%Error: t/t_covergroup_autobins_bad.v:60:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
: ... note: In instance 't'
60 | ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]};
| ^
%Error: t/t_covergroup_autobins_bad.v:62:23: Non-constant expression in bin range; range bounds must be constants
: ... note: In instance 't'
62 | bins b_nc_ub = {[size_var:$]};
| ^
%Error: t/t_covergroup_autobins_bad.v:63:23: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
: ... note: In instance 't'
63 | bins b_xz_ub = {[4'bxxxx:$]};
| ^
%Error: Exiting due to

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,75 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Tests for automatic bins error conditions
module t;
int size_var;
logic [3:0] cp_expr;
// Error: array size must be a constant
covergroup cg1;
cp1: coverpoint cp_expr {
bins auto[size_var];
}
endgroup
// Error: array size must be >= 1 (zero)
covergroup cg2;
cp1: coverpoint cp_expr {
bins auto[0];
}
endgroup
// Error: array size exceeds limit of 1000
covergroup cg2b;
cp1: coverpoint cp_expr {
bins auto[1001];
}
endgroup
// Error: non-constant value in bin ranges
covergroup cg3;
cp1: coverpoint cp_expr {
bins b[] = {[size_var:size_var]}; // non-constant array bins range (both bounds non-const)
bins b_mixed[] = {[0:size_var]}; // non-constant array bins range (max bound non-const)
bins b_range = {[size_var:4]}; // non-constant regular bin range (lhs non-const)
bins b_range2 = {[0:size_var]}; // non-constant regular bin range (rhs non-const)
bins b2 = {size_var}; // non-constant simple bin value
ignore_bins ign = {size_var}; // non-constant ignore_bins value
ignore_bins ign_range = {[0:size_var]}; // non-constant ignore_bins range (rhs non-const)
}
endgroup
// Error: non-constant coverpoint option value
covergroup cg4;
cp1: coverpoint cp_expr {
option.at_least = size_var; // non-constant coverpoint option value
}
endgroup
// Error: four-state (x/z) value in bin range bound, and non-constant lower bound
covergroup cg5;
cp1: coverpoint cp_expr {
bins b_xz = {[4'bxxxx:4'hF]}; // four-state lower bound (match-code path)
ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]}; // four-state lower bound (range-enum path)
ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]}; // four-state upper bound (range-enum path)
ignore_bins ign_nclo = {[size_var:4]}; // non-constant lower bound
bins b_nc_ub = {[size_var:$]}; // non-constant lower bound, open-ended '$' upper
bins b_xz_ub = {[4'bxxxx:$]}; // four-state lower bound, open-ended '$' upper
}
endgroup
cg1 cg1_inst = new;
cg2 cg2_inst = new;
cg2b cg2b_inst = new;
cg3 cg3_inst = new;
cg4 cg4_inst = new;
cg5 cg5_inst = new;
initial $finish;
endmodule

View File

@ -0,0 +1,18 @@
cg.data.low: 3
cg.data.zero: 1
cg_db.cp.high: 1
cg_db.cp.low: 1
cg_mixed.opcode.arith: 1
cg_mixed.opcode.load: 1
cg_mixed.opcode.nop: 1
cg_mixed.opcode.other: 1
cg_mixed.opcode.store: 1
cg_sel.cp.hi: 1
cg_sel.cp.lo: 1
cg_unbounded.cp.lo: 1
cg_unbounded.cp.others: 1
cg_unbounded_all.cp.all: 1
cg_unbounded_lo.cp.rest: 1
cg_unbounded_lo.cp.start: 1
cg_unbounded_signed.cp.hi: 1
cg_unbounded_signed.cp.lo: 1

View File

@ -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
import coverage_covergroup_common
test.scenarios('vlt')
coverage_covergroup_common.run(test)
# Verify coverage.dat format contains covergroup entries (replaces t_covergroup_database)
test.file_grep(test.coverage_filename, r'covergroup')
test.file_grep(test.coverage_filename, r'bin.{0,2}low')
test.file_grep(test.coverage_filename, r'bin.{0,2}high')
test.file_grep(test.coverage_filename, r'cg_db\.cp\.low')
test.file_grep(test.coverage_filename, r'cg_db\.cp\.high')
test.file_grep(test.coverage_filename, r'.*bin.{0,2}low.*\' [1-9]')
test.file_grep(test.coverage_filename, r'.*bin.{0,2}high.*\' [1-9]')

View File

@ -0,0 +1,163 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Tests bin counts, mixed bin types (single values, lists, ranges), and
// coverage database format (verifies coverage.dat contains covergroup entries).
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [3:0] data;
logic [7:0] opcode;
logic signed [3:0] sdata;
typedef struct packed {bit [7:0] value;} f_t;
f_t f1;
// cg: basic bin count tracking
covergroup cg;
coverpoint data {
bins zero = {0};
bins low = {[1:3]};
}
endgroup
// cg_mixed: mixed bin types - single values, multi-value lists, ranges
covergroup cg_mixed;
coverpoint opcode {
bins nop = {8'h00};
bins load = {8'h01, 8'h02, 8'h03};
bins store = {8'h04, 8'h05};
bins arith = {[8'h10:8'h1F]};
bins other = {[8'h20:8'hFE]};
}
endgroup
// cg_db: labeled coverpoint - verifies the coverage database records the correct hierarchy path
covergroup cg_db;
cp: coverpoint data {
bins low = {[0:3]};
bins high = {[8:15]};
}
endgroup
// cg_unbounded: open-ended bin range - '$' resolves to the coverpoint domain max (15 for 4-bit)
covergroup cg_unbounded;
cp: coverpoint data {
bins lo = {[0:9]}; // 1 bin: values 0..9
bins others = {[10:$]}; // 1 bin: values 10..15 ($ == domain max)
}
endgroup
// cg_unbounded_lo: open-ended LOWER bound - '$' resolves to the domain min (0)
covergroup cg_unbounded_lo;
cp: coverpoint data {
bins start = {[$:5]}; // 1 bin: values 0..5 ($ == domain min) -> expr <= 5
bins rest = {[6:15]};
}
endgroup
// cg_unbounded_all: both bounds open ('[$:$]' covers the entire domain -> always true)
covergroup cg_unbounded_all;
cp: coverpoint data {
bins all = {[$:$]}; // 1 bin: every value
}
endgroup
// cg_unbounded_signed: signed coverpoint with open-ended bounds in both directions
covergroup cg_unbounded_signed;
cp: coverpoint sdata {
bins hi = {[2:$]}; // sdata >= 2 (signed >=)
bins lo = {[$:-2]}; // sdata <= -2 (signed <=)
}
endgroup
// cg_sel: coverpoint over a struct-member part-select expression (AstSel)
covergroup cg_sel;
cp: coverpoint f1.value[3:0] { // low nibble only; upper bits ignored
bins lo = {[0:7]};
bins hi = {[8:15]};
}
endgroup
cg cg_inst;
cg_mixed cg_mixed_inst;
cg_db cg_db_inst;
cg_unbounded cg_unbounded_inst;
cg_unbounded_lo cg_unbounded_lo_inst;
cg_unbounded_all cg_unbounded_all_inst;
cg_unbounded_signed cg_unbounded_signed_inst;
cg_sel cg_sel_inst;
initial begin
cg_inst = new;
cg_mixed_inst = new;
cg_db_inst = new;
cg_unbounded_inst = new;
cg_unbounded_lo_inst = new;
cg_unbounded_all_inst = new;
cg_unbounded_signed_inst = new;
cg_sel_inst = new;
data = 0; cg_inst.sample(); // zero: 1
`checkr(cg_inst.get_inst_coverage(), 50.0);
data = 1; cg_inst.sample(); // low: 1
`checkr(cg_inst.get_inst_coverage(), 100.0);
data = 2; cg_inst.sample(); // low: 2
data = 2; cg_inst.sample(); // low: 3
opcode = 8'h00; cg_mixed_inst.sample(); // nop
`checkr(cg_mixed_inst.get_inst_coverage(), 20.0);
opcode = 8'h02; cg_mixed_inst.sample(); // load
`checkr(cg_mixed_inst.get_inst_coverage(), 40.0);
opcode = 8'h05; cg_mixed_inst.sample(); // store
`checkr(cg_mixed_inst.get_inst_coverage(), 60.0);
opcode = 8'h15; cg_mixed_inst.sample(); // arith
`checkr(cg_mixed_inst.get_inst_coverage(), 80.0);
opcode = 8'h80; cg_mixed_inst.sample(); // other
`checkr(cg_mixed_inst.get_inst_coverage(), 100.0);
data = 1; cg_db_inst.sample(); // low
`checkr(cg_db_inst.get_inst_coverage(), 50.0);
data = 10; cg_db_inst.sample(); // high
`checkr(cg_db_inst.get_inst_coverage(), 100.0);
// Open-ended range: '$' upper bound covers values up to the domain max
data = 5; cg_unbounded_inst.sample(); // lo
`checkr(cg_unbounded_inst.get_inst_coverage(), 50.0);
data = 12; cg_unbounded_inst.sample(); // others ([10:$] covers 12)
`checkr(cg_unbounded_inst.get_inst_coverage(), 100.0);
// Open-ended lower bound: '$' min covers values down to 0
data = 3; cg_unbounded_lo_inst.sample(); // start ([$:5] covers 3)
`checkr(cg_unbounded_lo_inst.get_inst_coverage(), 50.0);
data = 10; cg_unbounded_lo_inst.sample(); // rest
`checkr(cg_unbounded_lo_inst.get_inst_coverage(), 100.0);
// Both-open range '[$:$]' matches any value
data = 7; cg_unbounded_all_inst.sample(); // all
`checkr(cg_unbounded_all_inst.get_inst_coverage(), 100.0);
// Signed open-ended bounds: '$' resolves to signed domain max/min
sdata = 5; cg_unbounded_signed_inst.sample(); // hi ([2:$] covers 5)
`checkr(cg_unbounded_signed_inst.get_inst_coverage(), 50.0);
sdata = -5; cg_unbounded_signed_inst.sample(); // lo ([$:-2] covers -5)
`checkr(cg_unbounded_signed_inst.get_inst_coverage(), 100.0);
// Part-select coverpoint: only the low nibble is sampled (upper bits ignored)
f1.value = 8'h03; cg_sel_inst.sample(); // nibble 3 -> lo
`checkr(cg_sel_inst.get_inst_coverage(), 50.0);
f1.value = 8'hF9; cg_sel_inst.sample(); // nibble 9 -> hi (upper bits F ignored)
`checkr(cg_sel_inst.get_inst_coverage(), 100.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,4 @@
cg.cp_data.one: 1
cg.cp_data.three: 1
cg.cp_data.two: 1
cg.cp_data.zero: 2

View File

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

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Test covergroup clocked (automatic) sampling
// Tests --no-timing (default) mode; see t_covergroup_auto_sample_timing for --timing variant.
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [1:0] data;
// Covergroup with automatic sampling on posedge clk
covergroup cg @(posedge clk);
cp_data: coverpoint data {
bins zero = {2'b00};
bins one = {2'b01};
bins two = {2'b10};
bins three = {2'b11};
}
endgroup
cg cg_inst = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
0: data <= 2'b00;
1: data <= 2'b01;
2: data <= 2'b10;
3: data <= 2'b11;
4: begin
$write("*-* All Finished *-*\n");
$finish;
end
endcase
// NOTE: NO manual .sample() call - relying on automatic sampling!
end
endmodule

View File

@ -0,0 +1,40 @@
%Warning-COVERIGN: t/t_covergroup_coverpoint_method_unsup.v:21:32: Unsupported: 'bins' explicit array size (treated as '[]')
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
| ^
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: t/t_covergroup_coverpoint_method_unsup.v:31:42: Member 'a' not found in covergroup 'cg'
: ... note: In instance 't'
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
| ^~~~~~~~~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_covergroup_coverpoint_method_unsup.v:32:42: Member 'b' not found in covergroup 'cg'
: ... note: In instance 't'
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
| ^~~~~~~~~~~~~~~~~
%Error: t/t_covergroup_coverpoint_method_unsup.v:33:18: Member 'a' not found in covergroup 'cg'
: ... note: In instance 't'
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
| ^~~~~~~~~~~~~~~~~
%Error: t/t_covergroup_coverpoint_method_unsup.v:34:18: Member 'b' not found in covergroup 'cg'
: ... note: In instance 't'
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
| ^~~~~~~~~~~~~~~~~
%Error: Exiting due to

View File

@ -1,52 +0,0 @@
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:17: Ignoring unsupported: coverage clocking event
19 | covergroup cg @(posedge clk);
| ^
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:20:5: Ignoring unsupported: coverpoint
20 | coverpoint a;
| ^~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:36: Ignoring unsupported: cover bin specification
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
| ^
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:5: Ignoring unsupported: coverpoint
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
| ^~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:3: Ignoring unsupported: covergroup
19 | covergroup cg @(posedge clk);
| ^~~~~~~~~~
%Error: t/t_covergroup_coverpoints_unsup.v:31:42: Member 'a' not found in covergroup 'cg'
: ... note: In instance 't'
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
| ^~~~~~~~~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_covergroup_coverpoints_unsup.v:32:42: Member 'b' not found in covergroup 'cg'
: ... note: In instance 't'
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
| ^~~~~~~~~~~~~~~~~
%Error: t/t_covergroup_coverpoints_unsup.v:33:18: Member 'a' not found in covergroup 'cg'
: ... note: In instance 't'
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
| ^~~~~~~~~~~~~~~~~
%Error: t/t_covergroup_coverpoints_unsup.v:34:18: Member 'b' not found in covergroup 'cg'
: ... note: In instance 't'
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
| ^
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
| ^~~~~~~~~~~~~~~~~
%Error: Exiting due to

View File

@ -0,0 +1,108 @@
cg2.addr_cmd.addr0_x_read [cross]: 1
cg2.addr_cmd.addr0_x_write [cross]: 1
cg2.addr_cmd.addr1_x_read [cross]: 1
cg2.addr_cmd.addr1_x_write [cross]: 1
cg2.cp_addr.addr0: 2
cg2.cp_addr.addr1: 2
cg2.cp_cmd.read: 2
cg2.cp_cmd.write: 2
cg3.addr_cmd_mode.addr0_x_read_x_debug [cross]: 0
cg3.addr_cmd_mode.addr0_x_read_x_normal [cross]: 1
cg3.addr_cmd_mode.addr0_x_write_x_debug [cross]: 1
cg3.addr_cmd_mode.addr0_x_write_x_normal [cross]: 0
cg3.addr_cmd_mode.addr1_x_read_x_debug [cross]: 0
cg3.addr_cmd_mode.addr1_x_read_x_normal [cross]: 0
cg3.addr_cmd_mode.addr1_x_write_x_debug [cross]: 0
cg3.addr_cmd_mode.addr1_x_write_x_normal [cross]: 1
cg3.addr_cmd_mode.addr2_x_read_x_debug [cross]: 1
cg3.addr_cmd_mode.addr2_x_read_x_normal [cross]: 0
cg3.addr_cmd_mode.addr2_x_write_x_debug [cross]: 0
cg3.addr_cmd_mode.addr2_x_write_x_normal [cross]: 0
cg3.cp_addr.addr0: 2
cg3.cp_addr.addr1: 1
cg3.cp_addr.addr2: 1
cg3.cp_cmd.read: 2
cg3.cp_cmd.write: 2
cg3.cp_mode.debug: 2
cg3.cp_mode.normal: 2
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even [cross]: 1
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even [cross]: 1
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd [cross]: 1
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even [cross]: 0
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd [cross]: 1
cg4.cp_addr.addr0: 2
cg4.cp_addr.addr1: 2
cg4.cp_cmd.read: 2
cg4.cp_cmd.write: 2
cg4.cp_mode.debug: 2
cg4.cp_mode.normal: 2
cg4.cp_parity.even: 2
cg4.cp_parity.odd: 2
cg5.addr_cmd_opt.addr0_x_read [cross]: 1
cg5.addr_cmd_opt.addr0_x_write [cross]: 0
cg5.addr_cmd_opt.addr1_x_read [cross]: 0
cg5.addr_cmd_opt.addr1_x_write [cross]: 1
cg5.cp_addr.addr0: 1
cg5.cp_addr.addr1: 1
cg5.cp_cmd.read: 1
cg5.cp_cmd.write: 1
cg_at_least.addr_cmd_al.addr0_x_read [cross]: 1
cg_at_least.addr_cmd_al.addr0_x_write [cross]: 0
cg_at_least.addr_cmd_al.addr1_x_read [cross]: 0
cg_at_least.addr_cmd_al.addr1_x_write [cross]: 1
cg_at_least.cp_addr.addr0: 1
cg_at_least.cp_addr.addr1: 1
cg_at_least.cp_cmd.read: 1
cg_at_least.cp_cmd.write: 1
cg_goal.addr_cmd_goal.addr0_x_read [cross]: 1
cg_goal.addr_cmd_goal.addr0_x_write [cross]: 0
cg_goal.addr_cmd_goal.addr1_x_read [cross]: 0
cg_goal.addr_cmd_goal.addr1_x_write [cross]: 1
cg_goal.cp_addr.addr0: 1
cg_goal.cp_addr.addr1: 1
cg_goal.cp_cmd.read: 1
cg_goal.cp_cmd.write: 1
cg_ignore.cp_addr.a0: 2
cg_ignore.cp_addr.a1: 2
cg_ignore.cp_addr.ign [ignore]: 1
cg_ignore.cp_cmd.read: 3
cg_ignore.cp_cmd.write: 2
cg_ignore.cross_ab.a0_x_read [cross]: 1
cg_ignore.cross_ab.a0_x_write [cross]: 1
cg_ignore.cross_ab.a1_x_read [cross]: 1
cg_ignore.cross_ab.a1_x_write [cross]: 1
cg_range.addr_cmd_range.hi_range_x_read [cross]: 1
cg_range.addr_cmd_range.hi_range_x_write [cross]: 1
cg_range.addr_cmd_range.lo_range_x_read [cross]: 1
cg_range.addr_cmd_range.lo_range_x_write [cross]: 1
cg_range.cp_addr.hi_range: 2
cg_range.cp_addr.lo_range: 2
cg_range.cp_cmd.read: 2
cg_range.cp_cmd.write: 2
cg_unnamed_cross.__cross7.a0_x_read [cross]: 1
cg_unnamed_cross.__cross7.a0_x_write [cross]: 0
cg_unnamed_cross.__cross7.a1_x_read [cross]: 0
cg_unnamed_cross.__cross7.a1_x_write [cross]: 1
cg_unnamed_cross.cp_a.a0: 1
cg_unnamed_cross.cp_a.a1: 1
cg_unnamed_cross.cp_c.read: 1
cg_unnamed_cross.cp_c.write: 1
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_read [cross]: 1
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_write [cross]: 0
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_read [cross]: 0
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_write [cross]: 1
cg_unsup_cross_opt.cp_addr.addr0: 1
cg_unsup_cross_opt.cp_addr.addr1: 1
cg_unsup_cross_opt.cp_cmd.read: 1
cg_unsup_cross_opt.cp_cmd.write: 1

View File

@ -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, verilator_flags2=['--Wno-COVERIGN'])

View File

@ -0,0 +1,264 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test cross coverage: 2-way, 3-way, and 4-way crosses
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [1:0] addr;
logic cmd;
logic mode;
logic parity;
// 2-way cross
covergroup cg2;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd: cross cp_addr, cp_cmd;
endgroup
// 3-way cross
covergroup cg3;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
bins addr2 = {2};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
cp_mode: coverpoint mode {
bins normal = {0};
bins debug = {1};
}
addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode;
endgroup
// 4-way cross
covergroup cg4;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
cp_mode: coverpoint mode {
bins normal = {0};
bins debug = {1};
}
cp_parity: coverpoint parity {
bins even = {0};
bins odd = {1};
}
addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity;
endgroup
// Cross with option set inside the cross body
covergroup cg5;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd_opt: cross cp_addr, cp_cmd {
option.weight = 2;
}
endgroup
// 2-way cross where one coverpoint uses a range bin
covergroup cg_range;
cp_addr: coverpoint addr {
bins lo_range = {[0:1]}; // range bin
bins hi_range = {[2:3]};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd_range: cross cp_addr, cp_cmd;
endgroup
// Cross where one coverpoint has ignore_bins - ignored values must not appear in cross bins
covergroup cg_ignore;
cp_addr: coverpoint addr {
ignore_bins ign = {3}; // addr=3 excluded from cross
bins a0 = {0};
bins a1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
cross_ab: cross cp_addr, cp_cmd;
endgroup
// Cross with option.at_least set in the cross body
covergroup cg_at_least;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd_al: cross cp_addr, cp_cmd {
option.at_least = 3;
}
endgroup
// Cross with option.goal set in the cross body
covergroup cg_goal;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd_goal: cross cp_addr, cp_cmd {
option.goal = 90;
}
endgroup
// Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it
covergroup cg_unsup_cross_opt;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
addr_cmd_unsup: cross cp_addr, cp_cmd {
option.per_instance = 1; // unsupported for cross - expect COVERIGN warning
}
endgroup
// Covergroup with an unnamed cross - the cross is reported under the default name "cross"
covergroup cg_unnamed_cross;
cp_a: coverpoint addr { bins a0 = {0}; bins a1 = {1}; }
cp_c: coverpoint cmd { bins read = {0}; bins write = {1}; }
cross cp_a, cp_c; // no label: reported under the default cross name
endgroup
cg2 cg2_inst = new;
cg_ignore cg_ignore_inst = new;
cg_range cg_range_inst = new;
cg3 cg3_inst = new;
cg4 cg4_inst = new;
cg5 cg5_inst = new;
cg_at_least cg_at_least_inst = new;
cg_goal cg_goal_inst = new;
cg_unsup_cross_opt cg_unsup_cross_opt_inst = new;
cg_unnamed_cross cg_unnamed_cross_inst = new;
initial begin
// Sample 2-way: hit all 4 combinations
// cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count)
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
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
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
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
// 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)
addr = 0; cmd = 0; mode = 0; cg3_inst.sample(); // addr0 x read x normal
addr = 1; cmd = 1; mode = 0; cg3_inst.sample(); // addr1 x write x normal
addr = 2; cmd = 0; mode = 1; cg3_inst.sample(); // addr2 x read x debug
addr = 0; cmd = 1; mode = 1; 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%
addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample();
addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample();
`checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins
addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample();
addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample();
`checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins
// 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%
addr = 0; cmd = 0; cg5_inst.sample();
`checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
addr = 1; cmd = 1; cg5_inst.sample();
`checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
// cg_ignore: 2+2+4=8 bins total
addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read
`checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8
addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write
`checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8
addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write
`checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8
addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8
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
addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read
`checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8
addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write
`checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8
addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write
`checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8
addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read
`checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8
// 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%
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);
// 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%
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);
// Sample cg_unsup_cross_opt
// cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
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);
// Sample cg_unnamed_cross
// cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
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);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,10 @@
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:10: Ignoring unsupported coverage cross option: 'per_instance'
13 | option.per_instance = 1;
| ^~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:15:35: Unsupported: cross of 'var_x' which is not a coverpoint (implicit coverpoint)
: ... note: In instance 't'
15 | cross_implicit: cross cp_a, var_x;
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This file ONLY is placed under the Creative Commons Public Domain, for
# any use, without warranty, 2025 by Wilson Snyder.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t;
covergroup cg;
cp_a: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
cp_b: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
cross_ab: cross cp_a, cp_b {
option.per_instance = 1; // unsupported for cross; triggers COVERIGN
}
cross_implicit: cross cp_a, var_x;
endgroup
logic var_x = 1'b0;
cg cg_i = new;
initial begin
cg_i.sample();
$finish;
end
endmodule

View File

@ -0,0 +1,36 @@
cg.data.high: 1
cg.data.low: 1
cg.data.other: 2
cg2.cp_only_default.all: 4
cg3.data.bad [ignore]: 1
cg3.data.err [illegal]: 0
cg3.data.normal: 2
cg3.data.other: 2
cg4.cp_idx.auto_0: 1
cg4.cp_idx.auto_1: 1
cg4.cp_idx.auto_2: 1
cg4.cp_idx.skip [ignore]: 0
cg5.cp_data64.auto[0]: 2
cg5.cp_data64.auto[1]: 0
cg5.cp_data64.auto[2]: 0
cg5.cp_data64.auto[3]: 0
cg6.cp_data65.hi: 1
cg6.cp_data65.lo: 1
cg7.data.hi: 1
cg7.data.lo: 1
cg_cross.a.auto_0: 1
cg_cross.a.auto_1: 1
cg_cross.ab.auto_0_x_auto_0 [cross]: 1
cg_cross.ab.auto_0_x_auto_1 [cross]: 0
cg_cross.ab.auto_1_x_auto_0 [cross]: 0
cg_cross.ab.auto_1_x_auto_1 [cross]: 1
cg_cross.b.auto_0: 1
cg_cross.b.auto_1: 1
cg_member.__Vcoverpoint0.auto_0: 1
cg_member.__Vcoverpoint0.auto_1: 0
cg_member.__Vcoverpoint1.auto_0: 0
cg_member.__Vcoverpoint1.auto_1: 1
cg_plain.a.auto_0: 1
cg_plain.a.auto_1: 0
cg_plain.b.auto_0: 0
cg_plain.b.auto_1: 1

View File

@ -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')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,206 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test default bins - catch-all for values not in other bins
// 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
// Non-covergroup class in the same module - must not interfere with covergroup processing
class DataHelper;
bit [7:0] val;
function new(bit [7:0] v); val = v; endfunction
endclass
module t;
bit [7:0] data;
logic [1:0] idx;
logic [63:0] data64;
logic [64:0] data65;
DataHelper helper;
// Signals for the unlabeled-coverpoint covergroups below
logic [1:0] a;
logic [1:0] b;
typedef struct packed {
logic [1:0] hi;
logic [1:0] lo;
} pair_t;
pair_t p;
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins high = {[12:15]};
bins other = default; // Catches everything else (4-11, 16+)
}
endgroup
// Covergroup with default as the only bin - catches all sampled values
covergroup cg2;
cp_only_default: coverpoint data {
bins all = default;
}
endgroup
// Covergroup with default + ignore + illegal bins - excluded values must not count toward coverage
covergroup cg3;
coverpoint data {
ignore_bins bad = {255}; // excluded from coverage
illegal_bins err = {254}; // illegal value, excluded from coverage
bins normal = {[1:10]};
bins other = default;
}
endgroup
// Auto-bins on a small range with one value excluded by ignore_bins -
// when the range is small enough, one auto-bin per valid value is created; the excluded value is skipped.
covergroup cg4;
cp_idx: coverpoint idx {
ignore_bins skip = {2}; // value 2 excluded; auto-bins created for 0,1,3
}
endgroup
// 64-bit signal with auto_bin_max=4
covergroup cg5;
cp_data64: coverpoint data64 { bins auto[4]; }
endgroup
// 65-bit signal with explicit range bins
covergroup cg6;
cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; }
endgroup
// Unlabeled coverpoint - the signal name is used as the coverpoint name
covergroup cg7;
coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; }
endgroup
// Multiple unlabeled coverpoints: each gets a unique deterministic name (the variable
// name for a single identifier). Previously two unlabeled coverpoints in one covergroup
// collided on the generated bin-variable name (e.g. duplicate '__Vcov__auto_0').
covergroup cg_plain;
option.auto_bin_max = 2;
coverpoint a;
coverpoint b;
endgroup
// Unlabeled coverpoints with compound (member-select) expressions get synthesized names.
covergroup cg_member;
option.auto_bin_max = 2;
coverpoint p.lo;
coverpoint p.hi;
endgroup
// A cross referencing unlabeled coverpoints by their derived names.
covergroup cg_cross;
option.auto_bin_max = 2;
coverpoint a;
coverpoint b;
ab: cross a, b;
endgroup
initial begin
cg cg_inst;
cg2 cg2_inst;
cg3 cg3_inst;
cg4 cg4_inst;
cg5 cg5_inst;
cg6 cg6_inst;
cg7 cg7_inst;
cg_plain cg_plain_inst;
cg_member cg_member_inst;
cg_cross cg_cross_inst;
cg_inst = new();
cg2_inst = new();
cg3_inst = new();
cg4_inst = new();
cg5_inst = new();
cg6_inst = new();
cg7_inst = new();
cg_plain_inst = new();
cg_member_inst = new();
cg_cross_inst = new();
helper = new(8'h42);
data = helper.val; // Use helper to avoid optimization
// Hit low bin
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
// Hit high bin
data = 14;
cg_inst.sample();
cg2_inst.sample();
// Hit default bin with value 7 (not in low or high)
data = 7;
cg_inst.sample();
cg2_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0); // all 3 bins (low, high, other) hit
// Hit another default value (should not increase coverage)
data = 20;
cg_inst.sample();
cg2_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0);
`checkr(cg2_inst.get_inst_coverage(), 100.0);
// Sample cg3: verify ignore/illegal bins do not contribute to coverage
data = 2; cg3_inst.sample(); // hits normal bin
`checkr(cg3_inst.get_inst_coverage(), 50.0); // 1/2 bins hit (normal)
data = 7; cg3_inst.sample(); // hits normal bin again
`checkr(cg3_inst.get_inst_coverage(), 50.0); // no new bins
data = 255; cg3_inst.sample(); // ignore_bins value; Verilator counts it toward default bin
`checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2: Verilator hits 'other' (default) even for ignore_bins
// note: do not sample 254 (illegal_bins would cause runtime assertion)
data = 100; cg3_inst.sample(); // hits default (other) bin
`checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2 bins hit
// Sample cg4: auto-bins with one excluded value
// idx=2 is in ignore_bins, so auto-bins cover 0, 1, 3 only (3 bins total)
idx = 0; cg4_inst.sample();
idx = 1; cg4_inst.sample();
idx = 3; cg4_inst.sample();
`checkr(cg4_inst.get_inst_coverage(), 100.0);
// Sample cg5: 64-bit signal, 4 auto bins; values 0 and 5 both fall in first bin
data64 = 0; cg5_inst.sample();
`checkr(cg5_inst.get_inst_coverage(), 25.0); // 1/4 bins hit
data64 = 5; cg5_inst.sample();
`checkr(cg5_inst.get_inst_coverage(), 25.0); // same bin, no increase
// Sample cg6: 65-bit signal with range bins
data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15]
`checkr(cg6_inst.get_inst_coverage(), 50.0);
data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200]
`checkr(cg6_inst.get_inst_coverage(), 100.0);
// Sample cg7: unlabeled coverpoint
data = 3; cg7_inst.sample(); // hits bin lo
`checkr(cg7_inst.get_inst_coverage(), 50.0);
data = 10; cg7_inst.sample(); // hits bin hi
`checkr(cg7_inst.get_inst_coverage(), 100.0);
// cg_plain: two unlabeled coverpoints (a, b), 2 auto bins each -> distinct names
a = 0; b = 3; cg_plain_inst.sample();
`checkr(cg_plain_inst.get_inst_coverage(), 50.0);
// cg_member: unlabeled member-select coverpoints (synthesized names)
p.lo = 0; p.hi = 3; cg_member_inst.sample();
`checkr(cg_member_inst.get_inst_coverage(), 50.0);
// cg_cross: cross of unlabeled coverpoints referenced by derived name
a = 0; b = 0; cg_cross_inst.sample();
a = 3; b = 3; cg_cross_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,7 @@
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:27:35: Unsupported: 'covergroup' coverpoint referencing enclosing class member; ignoring covergroup '__vlAnonCG_cov_trans'
: ... note: In instance 't'
27 | trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;}
| ^~~~~~~~~~~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -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(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test the graceful-degradation safety net for embedded covergroups (the dominant
// UVM pattern: a covergroup declared inside a class whose coverpoints reference the
// enclosing object's members). Such a covergroup is lowered into a sibling class
// with no handle to the enclosing instance, so emitting it would produce
// uncompilable C++ ("invalid use of non-static data member"). Until the enclosing
// back-pointer feature exists, Verilator must emit a clean COVERIGN warning and skip
// lowering the covergroup, rather than crashing the C++ compile.
class ubus_transfer;
bit [15:0] addr;
bit read_write;
endclass
class ubus_master_monitor;
ubus_transfer trans_collected;
// Coverpoints reference 'trans_collected', a member of the enclosing class.
// A cross is included so the safety-net cleanup also exercises cross removal.
covergroup cov_trans;
trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;}
trans_dir : coverpoint trans_collected.read_write;
trans_addr_x_dir : cross trans_start_addr, trans_dir;
endgroup
function new();
trans_collected = new;
cov_trans = new;
endfunction
endclass
module t;
ubus_master_monitor m;
initial begin
m = new;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,2 @@
Empty covergroup coverage: 100.000000%
*-* All Finished *-*

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,40 @@
// DESCRIPTION: Verilator: Verilog Test module - Edge case: empty covergroup
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test: Empty covergroup (no coverpoints)
// Expected: Should compile, coverage should be 100% (nothing to cover)
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [7:0] value;
// Empty covergroup - no coverpoints defined
covergroup cg_empty;
// Intentionally empty
endgroup
cg_empty cg_inst = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
value <= value + 1;
cg_inst.sample();
if (cyc == 5) begin
real cov;
cov = cg_inst.get_inst_coverage();
$display("Empty covergroup coverage: %f%%", cov);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -1,39 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
module t;
class base;
enum {red, green, blue} color;
covergroup g1 (bit [3:0] a) with function sample(bit b);
option.weight = 10;
option.per_instance = 1;
coverpoint a;
coverpoint b;
c: coverpoint color;
endgroup
function new();
g1 = new(0);
endfunction
endclass
class derived extends base;
bit d;
covergroup extends g1;
option.weight = 1; // overrides the weight from base g1
// uses per_instance = 1 from base g1
c: coverpoint color // overrides the c coverpoint in base g1
{
ignore_bins ignore = {blue};
}
coverpoint d; // adds new coverpoint
cross a, d; // crosses new coverpoint with inherited one
endgroup :g1
function new();
super.new();
endfunction
endclass
endmodule

View File

@ -1,39 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
module t;
class base;
function new();
g1 = new(0);
endfunction
enum {red, green, blue} color;
covergroup g1 (bit [3:0] a) with function sample(bit b);
option.weight = 10;
option.per_instance = 1;
coverpoint a;
coverpoint b;
c: coverpoint color;
endgroup
endclass
class derived extends base;
bit d;
function new();
super.new();
endfunction
covergroup extends g1;
option.weight = 1; // overrides the weight from base g1
// uses per_instance = 1 from base g1
c: coverpoint color // overrides the c coverpoint in base g1
{
ignore_bins ignore = {blue};
}
coverpoint d; // adds new coverpoint
cross a, d; // crosses new coverpoint with inherited one
endgroup :g1
endclass
endmodule

View File

@ -1,5 +1,5 @@
%Error: t/t_covergroup_func_override_bad.v:10:3: syntax error, unexpected function
10 | function sample();
%Error: t/t_covergroup_func_override_bad.v:9:3: syntax error, unexpected function
9 | function sample();
| ^~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
module t;
covergroup cg;
function sample();

View File

@ -0,0 +1,19 @@
cg_and.cp_concat.b00: 0
cg_and.cp_concat.b01: 1
cg_and.cp_concat.b10: 0
cg_and.cp_concat.b11: 0
cg_array_iff.cp.arr: 1
cg_bitw.cp.seven: 1
cg_default_iff.cp.def: 1
cg_default_iff.cp.known: 1
cg_iff.cp_value.disabled_hi: 0
cg_iff.cp_value.disabled_lo: 0
cg_iff.cp_value.enabled_hi: 1
cg_iff.cp_value.enabled_lo: 1
cg_or.cp.hi: 0
cg_or.cp.lo: 1
cg_part.cp.one: 1
cg_rel.cp.five: 1
cg_rel.cp.two: 0
cg_trans2_iff.cp.t2: 1
cg_trans3_iff.cp.t3: 1

View File

@ -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')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,219 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test iff (enable) guard: sampling is gated by the enable condition.
// Covers iff on explicit value bins, default bin, array bins,
// simple 2-step transition, and 3-step transition.
//
// Also covers compound iff expressions (&&, ||, unary !, bit/part-select,
// relational compare, parenthesized bitwise, and a concatenation-valued
// coverpoint with a compound iff). Previously any iff that was not a bare
// reference or a unary ! tripped an internal error ("Unexpected '...'
// expression under 'COVERPOINT'") because the iff child was widthed with no
// context.
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic enable;
int value;
// Signals for the compound-iff covergroups below
logic m_is_read;
logic [3:0] m_be;
logic [1:0] m_a;
logic [1:0] m_b;
int count;
// iff on explicit value bins
covergroup cg_iff;
cp_value: coverpoint value iff (enable) {
bins disabled_lo = {1};
bins disabled_hi = {2};
bins enabled_lo = {3};
bins enabled_hi = {4};
}
endgroup
// iff on default bin
covergroup cg_default_iff;
cp: coverpoint value iff (enable) {
bins known = {10};
bins def = default; // default bin with coverpoint-level iff
}
endgroup
// iff on array bins
covergroup cg_array_iff;
cp: coverpoint value iff (enable) {
bins arr[] = {5, 6, 7}; // array bins, all gated by iff
}
endgroup
// iff on 2-step transition
covergroup cg_trans2_iff;
cp: coverpoint value iff (enable) {
bins t2 = (1 => 2);
}
endgroup
// iff on 3-step transition
covergroup cg_trans3_iff;
cp: coverpoint value iff (enable) {
bins t3 = (1 => 2 => 3);
}
endgroup
// --- compound iff expressions ---
// unary ! combined with && and a bit-select, on a concatenation-valued
// coverpoint expression -- the exact reported shape.
covergroup cg_and;
cp_concat: coverpoint {m_a[0], m_b[0]} iff (!m_is_read && m_be[0]) {
bins b00 = {2'b00};
bins b01 = {2'b01};
bins b10 = {2'b10};
bins b11 = {2'b11};
}
endgroup
// logical || guard
covergroup cg_or;
cp: coverpoint count iff (m_be[0] || m_be[1]) {
bins lo = {1};
bins hi = {2};
}
endgroup
// part-select compare guard
covergroup cg_part;
cp: coverpoint count iff (m_be[3:0] != 0) {
bins one = {1};
}
endgroup
// relational compare guard
covergroup cg_rel;
cp: coverpoint count iff (count > 3) {
bins five = {5};
bins two = {2};
}
endgroup
// parenthesized bitwise guard
covergroup cg_bitw;
cp: coverpoint count iff ((m_a & m_b) == 2'b10) {
bins seven = {7};
}
endgroup
cg_iff cg1 = new;
cg_default_iff cg2 = new;
cg_array_iff cg3 = new;
cg_trans2_iff cg4 = new;
cg_trans3_iff cg5 = new;
cg_and ca = new;
cg_or co = new;
cg_part cpp = new;
cg_rel cr = new;
cg_bitw cb = new;
initial begin
// Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded
enable = 0;
value = 1; cg1.sample();
value = 2; cg1.sample();
`checkr(cg1.get_inst_coverage(), 0.0);
// Sample enabled_lo and enabled_hi with enable=1 -- must be recorded
enable = 1;
value = 3; cg1.sample();
`checkr(cg1.get_inst_coverage(), 25.0);
value = 4; cg1.sample();
`checkr(cg1.get_inst_coverage(), 50.0);
// cg2: default bin -- enable=1 lets known and default through
enable = 1;
value = 10; cg2.sample(); // hits 'known'
`checkr(cg2.get_inst_coverage(), 50.0);
value = 99; cg2.sample(); // hits 'def' (default)
`checkr(cg2.get_inst_coverage(), 100.0);
enable = 0;
value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def'
`checkr(cg2.get_inst_coverage(), 100.0);
// cg3: array bins with iff (3 bins: arr[5], arr[6], arr[7])
// 1/3 hit -> 33.3% (not a clean binary fraction; no checkr)
enable = 1;
value = 5; cg3.sample(); // arr[5] hit
enable = 0;
value = 6; cg3.sample(); // gated
// cg4: 2-step transition with iff
enable = 1;
value = 1; cg4.sample();
`checkr(cg4.get_inst_coverage(), 0.0);
value = 2; cg4.sample(); // (1=>2) hit with enable=1
`checkr(cg4.get_inst_coverage(), 100.0);
enable = 0;
value = 1; cg4.sample();
value = 2; cg4.sample(); // (1=>2) gated by iff
`checkr(cg4.get_inst_coverage(), 100.0);
// cg5: 3-step transition with iff
enable = 1;
value = 1; cg5.sample();
value = 2; cg5.sample(); // mid-sequence, enable=1
enable = 0;
value = 3; cg5.sample(); // iff is disabled at step 3 - incomplete sequence is discarded
`checkr(cg5.get_inst_coverage(), 0.0);
enable = 1;
value = 1; cg5.sample();
value = 2; cg5.sample();
value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1
`checkr(cg5.get_inst_coverage(), 100.0);
// --- compound iff expressions ---
// cg_and: guard true -> {0,1}=2'b01 sampled into b01
m_is_read = 0; m_be = 4'b0001; m_a = 2'b10; m_b = 2'b11; // m_a[0]=0,m_b[0]=1 -> 2'b01
ca.sample(); // b01 hit
`checkr(ca.get_inst_coverage(), 25.0);
m_is_read = 1; m_a = 2'b11; m_b = 2'b11; // guard false -> gated
ca.sample();
`checkr(ca.get_inst_coverage(), 25.0);
// cg_or: guard true via bit1
m_be = 4'b0010; count = 1; co.sample(); // lo hit
`checkr(co.get_inst_coverage(), 50.0);
m_be = 4'b0000; count = 2; co.sample(); // gated
`checkr(co.get_inst_coverage(), 50.0);
// cg_part: part-select != 0
m_be = 4'b1000; count = 1; cpp.sample(); // one hit
`checkr(cpp.get_inst_coverage(), 100.0);
m_be = 4'b0000; count = 1; cpp.sample(); // gated
`checkr(cpp.get_inst_coverage(), 100.0);
// cg_rel: count > 3
count = 5; cr.sample(); // five hit (5>3)
`checkr(cr.get_inst_coverage(), 50.0);
count = 2; cr.sample(); // two gated (2>3 false)
`checkr(cr.get_inst_coverage(), 50.0);
// cg_bitw: (m_a & m_b) == 2'b10
m_a = 2'b10; m_b = 2'b11; count = 7; cb.sample(); // seven hit
`checkr(cb.get_inst_coverage(), 100.0);
m_a = 2'b00; m_b = 2'b11; count = 7; cb.sample(); // gated
`checkr(cb.get_inst_coverage(), 100.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
cg.data.arr [ignore]: 0
cg.data.bad [illegal]: 0
cg.data.catch_all [ignore]: 0
cg.data.high: 1
cg.data.low: 1
cg.data.reserved [ignore]: 1
cg.data.wib [ignore]: 0
cg2.cp_auto.auto_0: 1
cg2.cp_auto.auto_1: 1
cg2.cp_auto.ign [ignore]: 2
cg2.cp_auto.ign_trans [ignore]: 1
cg2.cp_auto_ub.auto_0: 1
cg2.cp_auto_ub.auto_1: 1
cg2.cp_auto_ub.ub [ignore]: 2
cg2.cp_bounds.hi: 2
cg2.cp_bounds.lo: 2
cg2.cp_full.all: 4
cg3.cp_auto_lb.auto_0: 1
cg3.cp_auto_lb.auto_1: 1
cg3.cp_auto_lb.lb [ignore]: 1

View File

@ -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')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,90 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test ignore_bins - excluded from coverage
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [3:0] data;
logic [1:0] data2; // 2-bit signal for range-boundary tests
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins high = {[8:11]};
ignore_bins reserved = {[12:15]};
ignore_bins catch_all = default; // default ignore-bin: all values not in other bins are ignored
ignore_bins arr[] = {4, 5}; // array form: one ignore-bin per value
wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore-bin with don't-care bits
illegal_bins bad[] = {6, 7}; // illegal array form: one illegal-bin per value
}
endgroup
// cg2: ignore_bins using a range - auto-bins are created only for values not in the range.
// Also tests range-boundary conditions: when lo==0 or hi==maxVal, the range check simplifies.
// Also tests ignore_bins with a transition list.
covergroup cg2;
cp_auto: coverpoint data2 {
ignore_bins ign = {[2:3]}; // range ignore, no regular bins -> auto-bins created
ignore_bins ign_trans = (0 => 1); // ignore the 0->1 transition
}
cp_auto_ub: coverpoint data2 {
ignore_bins ub = {[2:$]}; // open-ended ignore: '$' == domain max (3) -> auto-bins for 0,1
}
cp_bounds: coverpoint data2 {
bins lo = {[0:1]}; // lower range (lo=0, no lower-bound check needed)
bins hi = {[2:3]}; // upper range (hi=maxVal for 2-bit, no upper-bound check needed)
}
cp_full: coverpoint data2 {
bins all = {[0:3]}; // full range (lo=0 and hi=maxVal: matches all values)
}
endgroup
// cg3: open-ended LOWER bound ignore - '$' == domain min (0) -> auto-bins for 2,3
covergroup cg3;
cp_auto_lb: coverpoint data2 {
ignore_bins lb = {[$:1]}; // ignore 0,1 -> auto-bins created for 2,3
}
endgroup
cg cg_inst;
cg2 cg2_inst;
cg3 cg3_inst;
initial begin
cg_inst = new;
cg2_inst = new;
cg3_inst = new;
data = 13; cg_inst.sample(); // reserved - ignored
`checkr(cg_inst.get_inst_coverage(), 0.0);
data = 1; cg_inst.sample(); // low
`checkr(cg_inst.get_inst_coverage(), 50.0);
data = 10; cg_inst.sample(); // high
`checkr(cg_inst.get_inst_coverage(), 100.0);
data2 = 0; cg2_inst.sample(); // auto_0, lo, all
data2 = 1; cg2_inst.sample(); // auto_1, lo, all
data2 = 2; cg2_inst.sample(); // ign, hi, all
`checkr(cg2_inst.get_inst_coverage(), 100.0);
data2 = 3; cg2_inst.sample(); // ign, hi, all
`checkr(cg2_inst.get_inst_coverage(), 100.0);
data2 = 0; cg3_inst.sample(); // lb (ignored)
data2 = 2; cg3_inst.sample(); // auto_0
`checkr(cg3_inst.get_inst_coverage(), 50.0);
data2 = 3; cg3_inst.sample(); // auto_1
`checkr(cg3_inst.get_inst_coverage(), 100.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,13 @@
cg.data.forbidden [illegal]: 0
cg.data.high: 1
cg.data.low: 1
cg.data.mid: 1
cg2.cp_arr.bad_arr [illegal]: 0
cg2.cp_arr.ok: 1
cg2.cp_arr.wlib [illegal]: 0
cg2.cp_trans.bad_2step [illegal]: 0
cg2.cp_trans.bad_3step [illegal]: 0
cg2.cp_trans.lib_default [illegal]: 0
cg2.cp_trans.ok: 1
cg3.cp.ign [ignore]: 2
cg3.cp.ill [illegal]: 0

View File

@ -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')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,76 @@
// 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.
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
logic [1:0] data;
logic [3:0] data4;
covergroup cg;
coverpoint data {
bins low = {0};
bins mid = {1};
bins high = {2};
illegal_bins forbidden = {3};
}
endgroup
// cg2: illegal_bins on multi-step transitions and array notation
covergroup cg2;
cp_trans: coverpoint data4 {
bins ok = {0};
illegal_bins bad_2step = (1 => 2); // 2-step illegal transition
illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition
illegal_bins lib_default = default; // illegal_bins = default
}
cp_arr: coverpoint data4 {
bins ok = {0};
illegal_bins bad_arr[] = {8, 9, 10}; // illegal array bins
wildcard illegal_bins wlib = {4'b1?00}; // wildcard illegal bins
}
endgroup
// cg3: all bins are ignore_bins or illegal_bins - get_coverage returns 100.0
covergroup cg3;
cp: coverpoint data {
ignore_bins ign = {0, 1};
illegal_bins ill = {2, 3};
}
endgroup
initial begin
automatic cg cg_inst = new;
automatic cg2 cg2_inst = new;
automatic cg3 cg3_inst = new;
// Sample legal values only
data = 0; cg_inst.sample();
data = 1; cg_inst.sample();
data = 2; cg_inst.sample();
`checkr(cg_inst.get_inst_coverage(), 100.0);
// Sample cg2 - only safe values, never triggering illegal bins
data4 = 0; cg2_inst.sample();
`checkr(cg2_inst.get_inst_coverage(), 100.0);
// Sample cg3 - values that only hit ignore_bins, never illegal_bins
data = 0; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 100.0);
data = 1; cg3_inst.sample();
`checkr(cg3_inst.get_inst_coverage(), 100.0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,19 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
class myClass;
covergroup embeddedCg;
endgroup
function new();
real r;
embeddedCg = new();
embeddedCg.sample();
r = embeddedCg.get_coverage();
endfunction
endclass

View File

@ -1,32 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
class myClass;
covergroup embeddedCg;
endgroup
function new();
real r;
embeddedCg = new();
embeddedCg.sample();
r = embeddedCg.get_coverage();
endfunction
endclass
class secondClass;
covergroup embeddedCg;
endgroup
function new();
real r;
embeddedCg = new();
embeddedCg.sample();
r = embeddedCg.get_coverage();
endfunction
endclass

View File

@ -1,8 +1,8 @@
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:14: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
13 | covergroup embeddedCg;
| ^~~~~~~~~~
t/t_covergroup_in_class_duplicate_bad.v:9:14: ... Location of original declaration
9 | covergroup embeddedCg;
| ^~~~~~~~~~
%Error: t/t_covergroup_in_class_duplicate_bad.v:12:3: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
12 | covergroup embeddedCg;
| ^~~~~~~~~~
t/t_covergroup_in_class_duplicate_bad.v:8:3: ... Location of original declaration
8 | covergroup embeddedCg;
| ^~~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
class myClass;
covergroup embeddedCg;

View File

@ -0,0 +1,4 @@
__vlAnonCG_embeddedCg.cp_mc.auto_0: 0
__vlAnonCG_embeddedCg.cp_mc.auto_1: 1
__vlAnonCG_embeddedCg.cp_sc.auto_0: 1
__vlAnonCG_embeddedCg.cp_sc.auto_1: 0

View File

@ -9,8 +9,8 @@
import vltest_bootstrap
import coverage_covergroup_common
test.scenarios('vlt')
test.compile()
test.passes()
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
class myClass;
covergroup embeddedCg;
cp_mc: coverpoint 1'b1;
endgroup
function new();
real cov_result;
embeddedCg = new();
embeddedCg.sample();
cov_result = embeddedCg.get_coverage();
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_coverage() out of range: %f", cov_result);
endfunction
endclass
class secondClass;
covergroup embeddedCg;
cp_sc: coverpoint 1'b0;
endgroup
function new();
real cov_result;
embeddedCg = new();
embeddedCg.sample();
cov_result = embeddedCg.get_coverage();
if (!(cov_result >= 0.0 && cov_result <= 100.0))
$error("%m: get_coverage() out of range: %f", cov_result);
endfunction
endclass
module t;
myClass mc;
secondClass sc;
initial begin
mc = new();
sc = new();
$finish;
end
endmodule

View File

@ -1,16 +0,0 @@
#!/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: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.passes()

View File

@ -1,15 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off COVERIGN */
class C;
covergroup embedded(int x) with function sample (int a, bit b);
endgroup
function new();
embedded = new(1);
embedded.sample(2, 1'b0);
endfunction
endclass

View File

@ -0,0 +1,7 @@
%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:11:5: Unsupported: 'covergroup' clocking event on member variable
: ... note: In instance 't'
11 | covergroup cov1 @m_z;
| ^~~~~~~~~~
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2024 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t(input clk);
class Packet;
int m_z;
int m_x;
covergroup cov1 @m_z;
coverpoint m_x;
endgroup
endclass
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,11 +0,0 @@
%Error: t/t_covergroup_method_bad.v:16:10: Member 'some_unknown_method' not found in covergroup 'cg'
: ... note: In instance 't'
16 | cov1.some_unknown_method.name = "new_cov1_name";
| ^~~~~~~~~~~~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error-UNSUPPORTED: t/t_covergroup_method_bad.v:16:30: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
: ... note: In instance 't'
16 | cov1.some_unknown_method.name = "new_cov1_name";
| ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -1,18 +0,0 @@
#!/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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename,
verilator_flags2=['--assert --error-limit 1000'],
fails=True)
test.passes()

View File

@ -1,20 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2023 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t;
// verilator lint_off COVERIGN
covergroup cg();
endgroup
cg cov1;
initial begin
cov1 = new;
cov1.some_unknown_method.name = "new_cov1_name"; // <-- BAD
$finish;
end
endmodule

View File

@ -0,0 +1,4 @@
cg.cp_neg.mixed: 4
cg.cp_neg.negative: 2
cg.cp_neg.positive: 3
cg.cp_neg.zero: 1

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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')
coverage_covergroup_common.run(test)

Some files were not shown because too many files have changed in this diff Show More