This commit is contained in:
Matthew Ballance 2026-04-08 06:46:50 -07:00 committed by GitHub
commit 0c12f214c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
152 changed files with 6615 additions and 959 deletions

View File

@ -129,9 +129,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` and
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`, `user` 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
Displays a help summary, the program version, and exits.

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:`Line Coverage`
- :ref:`Toggle Coverage`
@ -192,22 +193,60 @@ 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.
.. code-block:: sv
module top;
logic [7:0] addr;
logic cmd;
// Define a covergroup
covergroup cg;
cp_addr: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
endgroup
// Instantiate the covergroup
cg cg_inst = new;
always @(posedge clk) begin
// Sample coverage explicitly
cg_inst.sample();
end
endmodule
.. _line coverage:

View File

@ -70,6 +70,7 @@ set(HEADERS
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Covergroup.h
V3Dead.h
V3DebugBisect.h
V3Delayed.h
@ -237,6 +238,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

@ -619,11 +619,169 @@ public:
~ActiveVisitor() override = default;
};
//######################################################################
// Shared state for covergroup sampling passes
struct CovergroupState final {
std::unordered_map<const AstClass*, AstCFunc*> m_sampleFuncs; // Class -> sample CFunc
std::unordered_map<const AstClass*, AstSenTree*>
m_samplingEvents; // Class -> owned sampling event (if any)
};
//######################################################################
// Pass 1: collect sample CFuncs and sampling events from covergroup class scopes
class CovergroupCollectVisitor final : public VNVisitor {
// STATE
CovergroupState& m_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 {
if (AstClass* const classp = VN_CAST(nodep->modp(), Class)) {
if (classp->isCovergroup()) {
VL_RESTORER(m_classp);
m_classp = classp;
iterateChildren(nodep);
return;
}
}
iterateChildren(nodep);
}
void visit(AstCFunc* nodep) override {
if (!m_classp) return;
if (nodep->name().find("sample") != string::npos) {
m_state.m_sampleFuncs[m_classp] = nodep;
nodep->isCovergroupSample(true);
}
}
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 take ownership in the map for use during the second pass.
m_state.m_samplingEvents[m_classp] = nodep->eventp()->unlinkFrBack();
nodep->unlinkFrBack();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
CovergroupCollectVisitor(AstNetlist* nodep, CovergroupState& state)
: m_state{state} {
iterate(nodep);
}
~CovergroupCollectVisitor() override = default;
};
//######################################################################
// Pass 2: inject automatic sample() calls for covergroup instances
class CovergroupInjectVisitor final : public VNVisitor {
// STATE
CovergroupState& m_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();
if (!classp || !classp->isCovergroup()) return;
// Check if this covergroup has an automatic sampling event
const auto evtIt = m_state.m_samplingEvents.find(classp);
if (evtIt == m_state.m_samplingEvents.end())
return; // No automatic sampling for this covergroup
AstSenTree* const eventp = evtIt->second;
// V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc
const auto it = m_state.m_sampleFuncs.find(classp);
UASSERT_OBJ(it != m_state.m_sampleFuncs.end(), nodep,
"No sample() CFunc found for covergroup " << classp->name());
AstCFunc* const sampleCFuncp = it->second;
// 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(senTreep->deleteTree(), 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()});
UINFO(4, " Added automatic sample() call for covergroup "
<< varp->name()); // LCOV_EXCL_BR_LINE
}
void visit(AstActive*) override {} // Don't iterate into actives
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
CovergroupInjectVisitor(AstNetlist* nodep, CovergroupState& state)
: m_state{state} {
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
CovergroupState state;
CovergroupCollectVisitor{nodep, state}; // Pass 1: collect CFuncs and events
CovergroupInjectVisitor{nodep, state}; // Pass 2: inject sample() calls
for (const auto& itpair : state.m_samplingEvents) itpair.second->deleteTree();
}
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -1107,6 +1107,113 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
//######################################################################
class VCoverBinsType final {
public:
enum en : uint8_t {
BINS_USER, // Single bin with one or more values/ranges
BINS_ARRAY, // Array of bins with user-speciifed size
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
BINS_IGNORE, // Ignore bin
BINS_ILLEGAL, // Illegal bin
BINS_DEFAULT, // Default bin
BINS_WILDCARD, // Wildcard bin
BINS_TRANSITION // Transition bin
};
enum en m_e;
VCoverBinsType()
: m_e{BINS_USER} {}
// cppcheck-suppress noExplicitConstructor
constexpr VCoverBinsType(en _e)
: m_e{_e} {}
explicit VCoverBinsType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
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, const VCoverBinsType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VCoverBinsType::en lhs, const VCoverBinsType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
class VCoverOptionType final {
public:
enum en : uint8_t { WEIGHT, GOAL, AT_LEAST, AUTO_BIN_MAX, PER_INSTANCE, COMMENT };
enum en m_e;
VCoverOptionType()
: m_e{WEIGHT} {}
// cppcheck-suppress noExplicitConstructor
constexpr VCoverOptionType(en _e)
: m_e{_e} {}
explicit VCoverOptionType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[]
= {"weight", "goal", "at_least", "auto_bin_max", "per_instance", "comment"};
return names[m_e];
}
};
constexpr bool operator==(const VCoverOptionType& lhs, const VCoverOptionType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VCoverOptionType::en lhs, const VCoverOptionType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
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()
: m_e{NONE} {}
// cppcheck-suppress noExplicitConstructor
constexpr VTransRepType(en _e)
: m_e{_e} {}
explicit VTransRepType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
return names[m_e];
}
const char* asciiJson() const {
static const char* const names[] = {"", "\"consec\"", "\"goto\"", "\"noncons\""};
return names[m_e];
}
};
constexpr bool operator==(const VTransRepType& lhs, const VTransRepType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VTransRepType& lhs, VTransRepType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VTransRepType::en lhs, const VTransRepType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
class VDirection final {
public:
enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF };

View File

@ -721,6 +721,7 @@ public:
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isExprCoverageEligible() const override { return false; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), NodeAssign)); // V3Emit* assumption
return nullptr;

View File

@ -253,6 +253,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;
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& flag) override { m_name = flag; }
bool maybePointedTo() const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
@ -513,6 +527,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 = "")
@ -543,6 +558,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;
@ -618,6 +634,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 {
@ -1020,6 +1038,142 @@ 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
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{isWildcard ? VCoverBinsType::BINS_WILDCARD
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: VCoverBinsType::BINS_USER))} {
if (rangesp) 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 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} {
if (valuesp) 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) {
if (itemsp) 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} {
if (argsp) addArgsp(argsp);
if (sampleArgsp) addSampleArgsp(sampleArgsp);
if (membersp) 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
@ -2535,6 +2689,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);
if (binsp) 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 {
@ -2617,6 +2804,8 @@ class AstClass final : public AstNodeModule {
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
// 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)
@ -2644,6 +2833,9 @@ public:
void needRNG(bool flag) { m_needRNG = flag; }
bool useVirtualPublic() const { return m_useVirtualPublic; }
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
// 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

@ -3495,3 +3495,54 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
//######################################################################
// 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);
if (m_repType != VTransRepType::NONE) { 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

@ -797,7 +797,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);

1563
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

@ -282,6 +282,13 @@ class DataflowOptimize final {
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
return;
}
// TODO: remove once Actives can tolerate NEVER SenItems
if (AstSenItem* senItemp = VN_CAST(nodep, SenItem)) {
senItemp->foreach([](const AstVarRef* refp) {
DfgVertexVar::setHasExtRdRefs(refp->varScopep());
});
return;
}
// Check direct references
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(refp->varScopep());

View File

@ -267,7 +267,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
void visit(AstCReset* /*nodep*/) override { puts("/*CRESET*/"); }
void visit(AstCase* nodep) override {
putfs(nodep, "");
if (nodep->priorityPragma()) puts("priority ");
@ -307,6 +306,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 +810,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 +1014,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
}
}
void visit(AstClassRefDType* nodep) override {
putfs(nodep, nodep->classp() ? EmitCUtil::prefixNameProtect(
nodep->classp()) // LCOV_EXCL_BR_LINE - false: classp
// always set after linking
: nodep->prettyDTypeName(false));
}
void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); }
void visit(AstModport* nodep) override {
puts(nodep->verilogKwd());

View File

@ -130,6 +130,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
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
@ -213,6 +214,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; }
void saveJsonPtrFieldName(const std::string& fieldName);

View File

@ -1118,6 +1118,263 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
// 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, 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) {
// Find the 'new' function to add parameters to.
// At this point the only AstFunc in the class is the "new" constructor
// created just above; other members are AstVar or AstCovergroup sentinel.
AstFunc* newFuncp = nullptr;
for (AstNode* memberp = nodep->membersp(); memberp; memberp = memberp->nextp()) {
if (AstFunc* const funcp = VN_CAST(memberp, Func)) {
UASSERT_OBJ(
funcp->name() == "new", funcp,
"Unexpected non-new function in covergroup class during arg setup");
newFuncp = funcp;
break;
}
}
UASSERT_OBJ(newFuncp, nodep,
"Covergroup class must have a 'new' constructor function");
// Save the existing body statements and unlink them
AstNode* const existingBodyp = newFuncp->stmtsp();
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
// Add function parameters and assignments
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);
newFuncp->addStmtsp(paramp);
AstNodeExpr* const lhsp = new AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
newFuncp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
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};
if (sampleArgsp) {
for (AstNode* argp = sampleArgsp; 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 AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
}
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 {
// 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 (m_modp && VN_IS(m_modp, Class) && VN_CAST(m_modp, Class)->isCovergroup()) return;
// Transform raw parse-time AstCovergroup into a fully-formed AstClass
cleanFileline(nodep);
const string libname = m_modp ? 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 newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
newp->classMethod(true);
newp->isConstructor(true);
newp->dtypep(cgClassp->dtypep());
if (AstNode* const bodyp = nodep->membersp()) {
bodyp->unlinkFrBackWithNext();
newp->addStmtsp(bodyp);
}
cgClassp->addMembersp(newp);
}
// Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep)
createCovergroupMethods(cgClassp, nodep->argsp(), nodep->sampleArgsp());
// Replace AstCovergroup with AstClass and process the new class normally
nodep->replaceWith(cgClassp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
iterate(cgClassp);
}
void visit(AstCoverpoint* nodep) override {
cleanFileline(nodep);
// 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();
VCoverOptionType optType = VCoverOptionType::COMMENT;
if (optp->name() == "at_least") {
optType = VCoverOptionType::AT_LEAST;
} else if (optp->name() == "weight") {
optType = VCoverOptionType::WEIGHT;
} else if (optp->name() == "goal") {
optType = VCoverOptionType::GOAL;
} else if (optp->name() == "auto_bin_max") {
optType = VCoverOptionType::AUTO_BIN_MAX;
} else if (optp->name() == "per_instance") {
optType = VCoverOptionType::PER_INSTANCE;
} else if (optp->name() == "comment") {
optType = VCoverOptionType::COMMENT;
} else {
optp->v3warn(COVERIGN,
"Ignoring unsupported coverage option: " + optp->prettyNameQ());
}
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optType,
optp->valuep()->cloneTree(false)});
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. Nodes are properly in-tree here so
// unlinkFrBack() works cleanly with no bison-list hackery.
for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) {
nextp = itemp->nextp();
itemp->unlinkFrBack();
if (AstCoverOption* const optp = VN_CAST(itemp, CoverOption)) {
nodep->addOptionsp(optp);
} else {
// AstCgOptionAssign, AstFunc, and other unsupported items
VL_DO_DANGLING(itemp->deleteTree(), itemp);
}
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
// Default: Just iterate
cleanFileline(nodep);

View File

@ -296,6 +296,7 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst {
}
// VISITORS
void visit(AstNode* nodep) override {
// Push a new stack entry at the start of a list, but only if the list is not a
// single element (this saves a lot of allocations in expressions)

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:
@ -94,97 +96,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

@ -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);
}
@ -961,8 +965,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

@ -224,6 +224,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
@ -1744,8 +1745,20 @@ 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->name() == "auto_bin_max" && !nodep->typeOption()) {
// Extract constant value
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);
}
}
// 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(AstPow* nodep) override {
@ -3387,7 +3400,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());
@ -7460,6 +7482,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"
@ -236,6 +237,10 @@ static void process() {
// Before we do dead code elimination and inlining, or we'll lose it.
if (v3Global.opt.coverage()) 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,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
DECL_OPTION("-annotate-all", OnOff, &m_annotateAll);
DECL_OPTION("-annotate-min", Set, &m_annotateMin);
DECL_OPTION("-annotate-points", OnOff, &m_annotatePoints);
DECL_OPTION("-covergroup", OnOff, &m_covergroup);
DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); });
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
DECL_OPTION("-filter-type", Set, &m_filterType);
@ -143,6 +144,8 @@ int main(int argc, char** argv) {
V3Error::abortIfWarnings();
if (!top.opt.annotateOut().empty()) top.annotate(top.opt.annotateOut());
if (top.opt.covergroup()) top.covergroup();
if (top.opt.rank()) {
top.rank();
top.tests().dump(false);

View File

@ -39,6 +39,7 @@ class VlcOptions final {
bool m_annotateAll = false; // main switch: --annotate-all
int m_annotateMin = 10; // main switch: --annotate-min I<count>
bool m_annotatePoints = false; // main switch: --annotate-points
bool m_covergroup = false; // main switch: --covergroup
string m_filterType = "*"; // main switch: --filter-type
VlStringSet m_readFiles; // main switch: --read
bool m_rank = false; // main switch: --rank
@ -67,6 +68,7 @@ public:
int annotateMin() const { return m_annotateMin; }
bool countOk(uint64_t count) const { return count >= static_cast<uint64_t>(m_annotateMin); }
bool annotatePoints() const { return m_annotatePoints; }
bool covergroup() const { return m_covergroup; }
bool rank() const { return m_rank; }
bool unlink() const { return m_unlink; }
string writeFile() const { return m_writeFile; }

View File

@ -65,6 +65,11 @@ public:
string comment() const { return keyExtract(VL_CIK_COMMENT, m_name.c_str()); }
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
string type() const { return typeExtract(m_name.c_str()); }
// Covergroup-specific key accessors (long keys, no short-key alias)
string page() const { return keyExtract("page", m_name.c_str()); }
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 thresh() const {
// string as maybe ""
return keyExtract(VL_CIK_THRESH, m_name.c_str());

View File

@ -25,6 +25,9 @@
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>
@ -205,6 +208,171 @@ void VlcTop::rank() {
}
}
void VlcTop::covergroup() {
UINFO(2, "covergroup...");
// Structs for accumulating report data
struct BinEntry final {
std::string name;
std::string binType; // "ignore", "illegal", or "" (normal)
bool covered = false;
uint64_t count = 0;
};
struct CpEntry final {
std::string name;
bool isCross = false;
std::vector<BinEntry> bins;
uint64_t normalTotal = 0;
uint64_t normalCovered = 0;
};
struct CgEntry final {
std::string typeName;
std::string filename;
int lineno = 0;
std::vector<CpEntry> coverpoints;
std::map<std::string, size_t> cpIndex;
};
std::map<std::string, CgEntry> cgMap;
// Collect covergroup points from all loaded coverage data
for (const auto& nameNum : m_points) {
const VlcPoint& pt = m_points.pointNumber(nameNum.second);
if (pt.type() != "covergroup") continue;
const std::string page = pt.page();
// Page format: "v_covergroup/<cgTypeName>"
const std::string pagePrefix = "v_covergroup/";
if (page.size() <= pagePrefix.size()) continue;
const std::string cgTypeName = page.substr(pagePrefix.size());
// Parse hier: "<cg_type>.<cp_name>.<bin_name>"
const std::string hier = pt.hier();
const size_t dot1 = hier.find('.');
if (dot1 == std::string::npos) continue;
const size_t dot2 = hier.find('.', dot1 + 1);
if (dot2 == std::string::npos) continue;
const std::string cpName = hier.substr(dot1 + 1, dot2 - dot1 - 1);
const std::string binName = hier.substr(dot2 + 1);
auto& cg = cgMap[cgTypeName];
if (cg.typeName.empty()) {
cg.typeName = cgTypeName;
cg.filename = pt.filename();
cg.lineno = pt.lineno();
}
auto it = cg.cpIndex.find(cpName);
size_t cpIdx;
if (it == cg.cpIndex.end()) {
cpIdx = cg.coverpoints.size();
cg.cpIndex[cpName] = cpIdx;
CpEntry cp;
cp.name = cpName;
cp.isCross = pt.isCross();
cg.coverpoints.push_back(cp);
} else {
cpIdx = it->second;
}
BinEntry bin;
bin.name = binName;
bin.binType = pt.binType();
// Threshold: use per-bin thresh key (option.at_least) if present, else 1 (SV default)
const std::string threshStr = pt.thresh();
const uint64_t binThresh = threshStr.empty() ? 1 : std::stoull(threshStr);
bin.count = pt.count();
bin.covered = (bin.count >= binThresh);
cg.coverpoints[cpIdx].bins.push_back(bin);
if (bin.binType.empty()) {
++cg.coverpoints[cpIdx].normalTotal;
if (bin.covered) ++cg.coverpoints[cpIdx].normalCovered;
}
}
// Compute grand totals
uint64_t grandTotal = 0, grandCovered = 0, grandIgnored = 0, grandIllegal = 0;
for (const auto& cgPair : cgMap) {
for (const auto& cp : cgPair.second.coverpoints) {
grandTotal += cp.normalTotal;
grandCovered += cp.normalCovered;
for (const auto& bin : cp.bins) {
if (bin.binType == "ignore")
++grandIgnored;
else if (bin.binType == "illegal")
++grandIllegal;
}
}
}
// Format a percentage string "xx.xx"
const auto pctStr = [](uint64_t covered, uint64_t total) -> std::string {
std::ostringstream oss;
const double pct = (total == 0) ? 100.0 : (100.0 * covered / total);
oss << std::fixed << std::setprecision(2) << pct;
return oss.str();
};
const std::string divider(78, '-');
// Header and grand total
std::cout << "COVERGROUP COVERAGE REPORT\n";
std::cout << "==========================\n";
std::cout << "\n";
std::cout << "TOTAL: " << grandCovered << "/" << grandTotal << " bins covered ("
<< pctStr(grandCovered, grandTotal) << "%)\n";
if (grandIgnored || grandIllegal)
std::cout << " (" << grandIgnored << " ignored, " << grandIllegal << " illegal)\n";
// One section per covergroup type (map is sorted alphabetically)
for (const auto& cgPair : cgMap) {
const CgEntry& cg = cgPair.second;
uint64_t cgTotal = 0, cgCovered = 0;
for (const auto& cp : cg.coverpoints) {
cgTotal += cp.normalTotal;
cgCovered += cp.normalCovered;
}
std::cout << "\n" << divider << "\n";
std::cout << "Covergroup Type: " << cg.typeName << " [" << cg.filename << ":" << cg.lineno
<< "]\n";
std::cout << " Type Coverage: " << cgCovered << "/" << cgTotal << " bins ("
<< pctStr(cgCovered, cgTotal) << "%)\n";
for (const auto& cp : cg.coverpoints) {
std::cout << "\n";
std::cout << " " << (cp.isCross ? "Cross" : "Coverpoint") << ": " << cp.name << "\n";
std::cout << " Coverage: " << cp.normalCovered << "/" << cp.normalTotal << " bins ("
<< pctStr(cp.normalCovered, cp.normalTotal) << "%)\n";
std::cout << " Bins:\n";
// Align bin name column to max name length in this coverpoint
size_t maxNameLen = 0;
for (const auto& bin : cp.bins)
if (bin.name.size() > maxNameLen) maxNameLen = bin.name.size();
for (const auto& bin : cp.bins) {
const char* status;
if (bin.binType == "ignore")
status = "IGNORE ";
else if (bin.binType == "illegal")
status = "ILLEGAL";
else if (bin.covered)
status = "COVERED";
else
status = "ZERO ";
std::cout << " " << status << " " << std::left
<< std::setw(static_cast<int>(maxNameLen)) << bin.name << std::right
<< " " << bin.count << " hits\n";
}
}
}
std::cout << "\n" << divider << "\n";
}
//######################################################################
void VlcTop::annotateCalc() {

View File

@ -55,6 +55,7 @@ public:
// METHODS
void annotate(const string& dirname);
void covergroup();
void readCoverage(const string& filename, bool nonfatal = false);
void writeCoverage(const string& filename);
void writeInfo(const string& filename);

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
@ -6933,40 +6933,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>:
@ -7013,29 +7000,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
@ -7059,39 +7050,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
@ -7099,30 +7155,40 @@ 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); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($1, $3); }
| 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); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($1, $3); }
| 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; }
;
@ -7134,9 +7200,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
@ -7151,7 +7236,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
@ -7171,12 +7257,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
;
@ -7184,28 +7274,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
@ -7223,7 +7313,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:
;
@ -7242,7 +7332,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'");
@ -7253,7 +7343,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
@ -7751,11 +7841,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

@ -2802,6 +2802,41 @@ class VlTest:
self._file_contents_cache[filename] = str(fh.read())
return self._file_contents_cache[filename]
def covergroup_coverage_report(self, outfile: str = None) -> str:
"""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 = self.obj_dir + "/covergroup_report.txt"
contents = self.file_contents(self.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
@staticmethod
def _file_contents_static(filename: str) -> str:
if filename not in VlTest._file_contents_cache:

View File

@ -4,17 +4,23 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
// A plain (non-covergroup) class - exercises the non-covergroup class scope/varscope paths
class PlainClass;
int x;
endclass
// verilator lint_off COVERIGN
module t;
int i, j;
covergroup cg(int var1, int var2 = 42);
cp1: coverpoint i; // Non-empty body with args: exercises constructor-body path
endgroup
cg cov1 = new(69, 77);
cg cov2 = new(69);
int i, j;
real r;
PlainClass plain_inst = new; // Non-covergroup class instance: exercises early-return paths
function void x();
cov1.set_inst_name("the_inst_name");
@ -23,16 +29,14 @@ module t;
cov1.stop();
void'(cov2.get_coverage());
r = cov2.get_coverage();
r = cov2.get_coverage(i, j);
void'(cov2.get_coverage(i, j));
// verilator lint_off IGNOREDRETURN
cov2.get_inst_coverage();
// verilator lint_on IGNOREDRETURN
r = cov2.get_inst_coverage(i, j);
void'(cov2.get_inst_coverage(i, j));
cg::get_coverage();
r = cg::get_coverage();
r = cg::get_coverage(i, j);
void'(cg::get_coverage());
void'(cg::get_coverage(i, j));
endfunction
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,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage --Wno-COVERIGN'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,78 @@
// 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 InsideRange and AstRange
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: exercises InsideRange in array bins (e.g., bins r[] = {[0:3]})
covergroup cg2;
cp: coverpoint data {
bins range_arr[] = {[0:3]}; // InsideRange -> 4 separate bins
}
endgroup
// cg3: exercises AstRange in array bins (e.g., bins r[N] = {[lo:hi]})
covergroup cg3;
cp: coverpoint data {
bins range_sized[4] = {[4:7]}; // AstRange with explicit count
}
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();
// Hit second array bin value (5)
data = 5;
cg_inst.sample();
// Hit the grouped bin (covers all of 2, 6, 10)
data = 6;
cg_inst.sample();
// Hit third array bin value (9)
data = 9;
cg_inst.sample();
// Verify hitting other values in grouped bin doesn't increase coverage
data = 2;
cg_inst.sample();
// Hit range_arr bins (InsideRange [0:3])
data = 0; cg2_inst.sample();
data = 1; cg2_inst.sample();
data = 2; cg2_inst.sample();
// Hit range_sized bins (AstRange [4:7])
data = 4; cg3_inst.sample();
data = 5; cg3_inst.sample();
data = 6; cg3_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,19 @@
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

View File

@ -0,0 +1,21 @@
#!/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.compile(verilator_flags2=['--coverage', '-Wno-UNSIGNED', '-Wno-CMPCONST'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,73 @@
// 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
module t;
logic [2:0] data3;
logic [3:0] data4; // 4-bit signal for range-bin path tests
// 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 at *coverpoint* level (lines 207, 209)
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: range-bin skip path (lines 287, 356-359).
// 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
initial begin
cg1 cg1_inst;
cg2 cg2_inst;
cg3 cg3_inst;
cg4 cg4_inst;
cg1_inst = new;
cg2_inst = new;
cg3_inst = new;
cg4_inst = new;
data3 = 0; cg1_inst.sample();
data3 = 3; cg1_inst.sample();
data3 = 0; cg2_inst.sample();
data3 = 4; cg2_inst.sample();
data3 = 1; cg3_inst.sample();
data3 = 5; cg3_inst.sample();
// Sample valid (non-ignored) values for cg4
data4 = 4; cg4_inst.sample(); // [4:7] bin
data4 = 8; cg4_inst.sample(); // [8:11] bin
data4 = 12; cg4_inst.sample(); // [12:15] bin
$write("*-* All Finished *-*\n");
$finish;
end
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,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,73 @@
// 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
module t;
/* verilator lint_off CMPCONST */
logic [2:0] data; // 3-bit: 0-7
logic [3:0] data4; // 4-bit: exercises width<64 path in maxVal computation
logic [63:0] data64; // 64-bit: exercises width>=64 (UINT64_MAX) path
covergroup cg;
coverpoint data {
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
}
endgroup
// 4-bit auto bins: exercises (width < 64) path: maxVal = (1<<4)-1 = 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 ignore_bins: exercises excluded-value skip path
covergroup cg_4bit_excl;
coverpoint data4 {
ignore_bins bad = {0}; // value 0 excluded from auto expansion
bins auto[4];
}
endgroup
// auto_bin_max=2 on 64-bit: exercises numTotalValues=UINT64_MAX path
covergroup cg2;
option.auto_bin_max = 2;
coverpoint data64;
endgroup
/* verilator lint_on CMPCONST */
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
data = 0; cg_inst.sample();
data = 2; cg_inst.sample();
data = 5; cg_inst.sample();
data = 7; cg_inst.sample();
// Sample 4-bit bins
data4 = 0; cg4_inst.sample(); // bin [0:3]
data4 = 7; cg4_inst.sample(); // bin [4:7]
data4 = 10; cg4_inst.sample(); // bin [8:11]
data4 = 14; cg4_inst.sample(); // bin [12:15]
// Sample 4-bit with exclusion (value 0 excluded; bins start at 1)
data4 = 1; cg4e_inst.sample();
data4 = 8; cg4e_inst.sample();
// Sample both 64-bit range bins
data64 = 64'd0; cg2_inst.sample(); // auto[0]
data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample(); // auto[1]
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,29 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
// Simple test harness for t_covergroup_auto_sample - provides clock
//
// 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
#include "verilated.h"
#include "Vt_covergroup_auto_sample.h"
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vt_covergroup_auto_sample* top = new Vt_covergroup_auto_sample;
// Run for 20 cycles
for (int i = 0; i < 20; i++) {
top->clk = 0;
top->eval();
top->clk = 1;
top->eval();
if (Verilated::gotFinish()) break;
}
delete top;
return 0;
}

View File

@ -0,0 +1,21 @@
#!/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')
# Use the same .v file as the non-timing test
test.top_filename = "t/t_covergroup_clocked_sample.v"
test.compile(v_flags2=["--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,30 @@
%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:34:26: Non-constant expression in bin value list; values must be constants
: ... note: In instance 't'
34 | ignore_bins ign = {size_var};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:31:12: Non-constant expression in array bins range; range bounds must be constants
: ... note: In instance 't'
31 | bins b[] = {[size_var:size_var]};
| ^
%Error: t/t_covergroup_autobins_bad.v:32:12: Non-constant expression in array bins range; range bounds must be constants
: ... note: In instance 't'
32 | bins b_mixed[] = {[0:size_var]};
| ^~~~~~~
%Error: t/t_covergroup_autobins_bad.v:33:18: Non-constant expression in bin range; values must be constants
: ... note: In instance 't'
33 | bins b2 = {size_var};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:34:26: Non-constant expression in bin range; values must be constants
: ... note: In instance 't'
34 | ignore_bins ign = {size_var};
| ^~~~~~~~
%Error: Exiting due to

View File

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

View File

@ -0,0 +1,43 @@
// 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: 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 b2 = {size_var}; // non-constant simple bin value
ignore_bins ign = {size_var}; // non-constant ignore_bins value
}
endgroup
cg1 cg1_inst = new;
cg2 cg2_inst = new;
cg3 cg3_inst = new;
initial $finish;
endmodule

View File

@ -0,0 +1,2 @@
cg.data.low: 3
cg.data.zero: 1

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,33 @@
// 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 viewing individual bin hit counts
module t (/*AUTOARG*/);
logic [3:0] data;
covergroup cg;
coverpoint data {
bins zero = {0};
bins low = {[1:3]};
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
data = 0; cg_inst.sample(); // zero: 1
data = 1; cg_inst.sample(); // low: 1
data = 2; cg_inst.sample(); // low: 2
data = 2; cg_inst.sample(); // low: 3
$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,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

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,3 @@
cg.data.high: 1
cg.data.low: 1
cg.data.mid: 1

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,44 @@
// 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 querying coverage values via get_inst_coverage
module t (/*AUTOARG*/);
logic [3:0] data;
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:15]};
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
// Sample low bin - should be 33.3% (1 of 3 bins)
data = 1;
cg_inst.sample();
$display("After low: %0.1f%%", cg_inst.get_inst_coverage());
// Sample mid bin - should be 66.7% (2 of 3 bins)
data = 5;
cg_inst.sample();
$display("After mid: %0.1f%%", cg_inst.get_inst_coverage());
// Sample high bin - should be 100.0% (3 of 3 bins)
data = 10;
cg_inst.sample();
$display("After high: %0.1f%%", cg_inst.get_inst_coverage());
$write("*-* All Finished *-*\n");
$finish;
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,84 @@
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_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

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage --dumpi-tree 3 --dumpi-tree-json 3'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,169 @@
// 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
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 inside body: exercises addOptionsp in visit(AstCoverCross*)
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 with range bin: exercises lo!=hi path in buildBinCondition
covergroup cg_range;
cp_addr: coverpoint addr {
bins lo_range = {[0:1]}; // range bin (lo != hi) -> makeRangeCondition path
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: exercises BINS_USER FALSE branch
// in collectBins during cross code generation (L1139)
covergroup cg_ignore;
cp_addr: coverpoint addr {
ignore_bins ign = {3}; // BINS_IGNORE: not BINS_USER, exercises L1139 FALSE path
bins a0 = {0};
bins a1 = {1};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
cross_ab: cross cp_addr, cp_cmd;
endgroup
// Covergroup with unnamed cross: exercises crossName.empty() fallback to "cross" (L1395)
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 -> crossName is empty
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_unnamed_cross cg_unnamed_cross_inst = new;
initial begin
// Sample 2-way: hit all 4 combinations
addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read
addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write
addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write
addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read
// Sample 3-way: hit 4 of 12 combinations
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
addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample();
addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample();
addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample();
addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample();
// Sample cg5 (cross with option)
addr = 0; cmd = 0; cg5_inst.sample();
addr = 1; cmd = 1; cg5_inst.sample();
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read
addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write
addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write
addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read
addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins)
// Sample range-bin cross
addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read
addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write
addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write
addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read
// Sample cg_unnamed_cross: exercises unnamed cross (crossName fallback to "cross")
addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read
addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,30 @@
// 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
#include <verilated.h>
#include "Vt_covergroup_cross_large.h"
int main(int argc, char** argv) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->commandArgs(argc, argv);
const std::unique_ptr<Vt_covergroup_cross_large> topp{
new Vt_covergroup_cross_large{contextp.get()}};
topp->clk = 0;
while (!contextp->gotFinish() && contextp->time() < 100) {
topp->clk = !topp->clk;
topp->eval();
contextp->timeInc(1);
}
topp->final();
contextp->coveragep()->write();
return 0;
}

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.file_grep(test.coverage_filename, r'covergroup')
test.file_grep(test.coverage_filename, r'bin.{0,2}low') # binlow with possible delimiter
test.file_grep(test.coverage_filename, r'bin.{0,2}high') # binhigh with possible delimiter
test.file_grep(test.coverage_filename, r'cg\.cp\.low')
test.file_grep(test.coverage_filename, r'cg\.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]')
test.passes()

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// Test that functional coverage is properly written to coverage database
// Checks that coverage.dat contains covergroup entries with correct format
// Expected coverage database entries will contain:
// - Type "covergroup"
// - Bin names ("low", "high")
// - Hierarchy ("cg.cp.low", "cg.cp.high")
module t (/*AUTOARG*/);
logic [1:0] data;
covergroup cg;
cp: coverpoint data {
bins low = {2'b00};
bins high = {2'b11};
}
endgroup
cg cg_inst = new;
initial begin
// Sample both bins
data = 2'b00;
cg_inst.sample();
data = 2'b11;
cg_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
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

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,139 @@
// 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
// Non-covergroup class: exercises V3Active isCovergroup()=false branch
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; // 64-bit: exercises width>=64 auto-bin path (L139)
logic [64:0] data65; // 65-bit: exercises exprWidth>64 in makeRangeCondition
DataHelper helper; // Module-level class var: exercises V3Active isCovergroup()=false
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: exercises defaultCondp=BitTrue path
covergroup cg2;
cp_only_default: coverpoint data {
bins all = default;
}
endgroup
// Covergroup with default + ignore + illegal bins: exercises BINS_IGNORE/BINS_ILLEGAL
// skip paths in generateDefaultBinMatchCode (L558-L559)
covergroup cg3;
coverpoint data {
ignore_bins bad = {255}; // BINS_IGNORE skip path
illegal_bins err = {254}; // BINS_ILLEGAL skip path
bins normal = {[1:10]};
bins other = default;
}
endgroup
// Covergroup with auto-bins + ignore_bins on small range: exercises L295 excluded-value continue
// When numValidValues <= auto_bin_max, single-value auto-bins are created per value; the
// excluded.find() check at L295 fires for the ignore_bins value (idx=2).
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 4 auto-bins: exercises width>=64 branch in auto-bin range calculation
covergroup cg5;
cp_data64: coverpoint data64 { bins auto[4]; }
endgroup
// 65-bit signal with range bins: exercises exprWidth>64 path in makeRangeCondition
covergroup cg6;
cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; }
endgroup
// Unlabeled coverpoint: exercises cpName fallback via exprp()->name() (L1394-1398)
covergroup cg7;
coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; }
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_inst = new();
cg2_inst = new();
cg3_inst = new();
cg4_inst = new();
cg5_inst = new();
cg6_inst = new();
cg7_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();
// 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();
// Hit another default value (should not increase coverage)
data = 20;
cg_inst.sample();
cg2_inst.sample();
// Sample cg3: exercises BINS_IGNORE/BINS_ILLEGAL skip in default-bin detection loop
data = 2; cg3_inst.sample(); // hits normal bin
data = 7; cg3_inst.sample(); // hits normal bin again
data = 255; cg3_inst.sample(); // ignore_bins (not counted)
// note: do not sample 254 (illegal_bins would cause runtime assertion)
data = 100; cg3_inst.sample(); // hits default (other) bin
// Sample cg4: exercises auto-bin generation with excluded value (L295)
// idx=2 is in ignore_bins, so auto-bins cover 0,1,3 only
idx = 0; cg4_inst.sample();
idx = 1; cg4_inst.sample();
idx = 3; cg4_inst.sample();
// Sample cg5: 64-bit signal, sample into bin b[0] (value 0 is in first quarter)
data64 = 0; cg5_inst.sample();
data64 = 5; cg5_inst.sample();
// Sample cg6: 65-bit signal with range bins
data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15]
data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200]
// Sample cg7: unlabeled coverpoint (exercises exprp()->name() path)
data = 3; cg7_inst.sample(); // hits bin lo
data = 10; cg7_inst.sample(); // hits bin hi
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,2 @@
cg.data.high: 1
cg.data.low: 2

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,40 @@
// 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 dynamic covergroup creation with 'new' operator
module t;
covergroup cg;
coverpoint data {
bins low = {[0:1]};
bins high = {[2:3]};
}
endgroup
int data;
initial begin
cg cg_inst;
// Create first dynamic instance
cg_inst = new;
data = 0; cg_inst.sample(); // low bin
data = 2; cg_inst.sample(); // high bin
// Create second independent instance
begin
cg cg2;
cg2 = new;
data = 0; cg2.sample(); // low bin
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,29 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
// Simple test harness for t_covergroup_empty - provides clock
//
// 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
#include "verilated.h"
#include "Vt_covergroup_empty.h"
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vt_covergroup_empty* top = new Vt_covergroup_empty;
// Run for 20 cycles
for (int i = 0; i < 20; i++) {
top->clk = 0;
top->eval();
top->clk = 1;
top->eval();
if (Verilated::gotFinish()) break;
}
delete top;
return 0;
}

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,45 @@
// 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
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
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

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_covergroup_extends_newfirst.v:29:5: Unsupported: covergroup inheritance (extends) is not implemented
29 | covergroup extends g1;
| ^~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_covergroup_extends_unsup.v:26:5: Unsupported: covergroup inheritance (extends)
26 | covergroup extends g1;
| ^~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%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

@ -1,15 +1,13 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Covergroup inheritance with 'extends' is not yet supported
//
// 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;
@ -18,13 +16,13 @@ module t;
coverpoint b;
c: coverpoint color;
endgroup
function new();
g1 = new(0);
endfunction
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
@ -35,5 +33,8 @@ module t;
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

@ -0,0 +1,9 @@
cg_array_iff.cp.arr: 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_trans2_iff.cp.t2: 1
cg_trans3_iff.cp.t3: 1

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,107 @@
// 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.
module t;
logic enable;
int value;
// --- Original: iff on explicit value bins (lines 565-567) ---
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 (lines 633-635/641) ---
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 (lines 982-983) ---
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 (lines 1109-1110) ---
covergroup cg_trans2_iff;
cp: coverpoint value iff (enable) {
bins t2 = (1 => 2);
}
endgroup
// --- iff on 3-step transition (lines 724-725, 762-763) ---
covergroup cg_trans3_iff;
cp: coverpoint value iff (enable) {
bins t3 = (1 => 2 => 3);
}
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;
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();
// Sample enabled_lo and enabled_hi with enable=1 -- must be recorded
enable = 1;
value = 3; cg1.sample();
value = 4; cg1.sample();
// cg2: default bin -- enable=1 lets known and default through
enable = 1;
value = 10; cg2.sample(); // hits 'known'
value = 99; cg2.sample(); // hits 'def' (default)
enable = 0;
value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def'
// cg3: array bins with iff
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();
value = 2; cg4.sample(); // (1=>2) hit with enable=1
enable = 0;
value = 1; cg4.sample();
value = 2; cg4.sample(); // (1=>2) gated by iff
// 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 fails at step 3 -- triggers restart path (line 762-763)
enable = 1;
value = 1; cg5.sample();
value = 2; cg5.sample();
value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,12 @@
cg.data.catch_all [ignore]: 0
cg.data.high: 1
cg.data.low: 1
cg.data.reserved [ignore]: 1
cg.data.wib: 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_bounds.hi: 2
cg2.cp_bounds.lo: 2
cg2.cp_full.all: 4

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,62 @@
// 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
module t (/*AUTOARG*/);
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; // null rangesp: exercises generateBinMatchCode !fullCondp
wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore bins (L7084-7085)
}
endgroup
// Exercises:
// extractValuesFromRange AstInsideRange branch (ignore_bins range, no regular bins)
// createImplicitAutoBins with excluded range values
// makeRangeCondition: skipUpperCheck=true (hi=maxVal) and both-skip (BitTrue)
// ignore_bins with transition list (L7102-7104)
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_bins with transition (L7102-7104)
}
cp_bounds: coverpoint data2 {
bins lo = {[0:1]}; // lo=0: skipLowerCheck -> AstLte
bins hi = {[2:3]}; // hi=maxVal (2-bit): skipUpperCheck -> AstGte
}
cp_full: coverpoint data2 {
bins all = {[0:3]}; // lo=0 and hi=maxVal: both skip -> AstConst(BitTrue)
}
endgroup
cg cg_inst;
cg2 cg2_inst;
initial begin
cg_inst = new;
cg2_inst = new;
data = 13; cg_inst.sample(); // reserved - ignored
data = 1; cg_inst.sample(); // low
data = 10; cg_inst.sample(); // high
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
data2 = 3; cg2_inst.sample(); // ign, hi, all
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,11 @@
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: 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

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

View File

@ -0,0 +1,54 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Test that illegal_bins are excluded from coverage (like ignore_bins)
//
// 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
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: exercises illegal_bins with 3-step transition (lines 744-747) and
// illegal_bins array notation (lines 996-998)
covergroup cg2;
cp_trans: coverpoint data4 {
bins ok = {0};
illegal_bins bad_2step = (1 => 2); // 2-step illegal transition (simple path)
illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition
illegal_bins lib_default = default; // illegal_bins = default (L7123-7124)
}
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 (L7087-7088)
}
endgroup
initial begin
automatic cg cg_inst = new;
automatic cg2 cg2_inst = new;
// Sample legal values only
data = 0; cg_inst.sample();
data = 1; cg_inst.sample();
data = 2; cg_inst.sample();
// Sample cg2 - only safe values, never triggering illegal bins
data4 = 0; cg2_inst.sample();
$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,8 +1,8 @@
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:14: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:3: 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
| ^~~~~~~~~~
t/t_covergroup_in_class_duplicate_bad.v:9:3: ... Location of original declaration
9 | covergroup embeddedCg;
| ^~~~~~~~~~
| ^~~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.compile(verilator_flags2=['--coverage'])
test.passes()

View File

@ -4,17 +4,15 @@
// 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();
void'(embeddedCg.get_coverage());
endfunction
endclass
@ -24,9 +22,8 @@ class secondClass;
endgroup
function new();
real r;
embeddedCg = new();
embeddedCg.sample();
r = embeddedCg.get_coverage();
void'(embeddedCg.get_coverage());
endfunction
endclass

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

@ -11,8 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename,
verilator_flags2=['--assert --error-limit 1000'],
fails=True)
test.lint(expect_filename=test.golden_filename, verilator_flags2=['--assert'], 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,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,5 @@
cg.opcode.arith: 1
cg.opcode.load: 1
cg.opcode.nop: 1
cg.opcode.others: 1
cg.opcode.store: 1

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
test.covergroup_coverage_report()
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
test.passes()

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