parent
7e2fe64ae2
commit
2886291eba
|
|
@ -133,9 +133,13 @@ verilator_coverage Arguments
|
|||
.. option:: --filter-type <regex>
|
||||
|
||||
Skips records of coverage types that matches with <regex>
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `user`,
|
||||
`fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The default
|
||||
value is `\*`.
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
|
||||
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
|
||||
default value is `\*`.
|
||||
|
||||
The `covergroup` type represents SystemVerilog functional coverage
|
||||
including covergroups, coverpoints, bins, and cross coverage as defined
|
||||
in IEEE 1800-2023 Section 19.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ Verilator supports adding code to the Verilated model to support
|
|||
SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables
|
||||
all forms of coverage:
|
||||
|
||||
- :ref:`User Coverage`
|
||||
- :ref:`Property Coverage`
|
||||
- :ref:`Covergroup Coverage`
|
||||
- :ref:`FSM Coverage`
|
||||
- :ref:`Line Coverage`
|
||||
- :ref:`Toggle Coverage`
|
||||
|
|
@ -193,22 +194,34 @@ When a model with coverage is executed, it will create a coverage file for
|
|||
collection and later analysis, see :ref:`Coverage Collection`.
|
||||
|
||||
|
||||
.. _user coverage:
|
||||
.. _property coverage:
|
||||
|
||||
Functional Coverage
|
||||
-------------------
|
||||
Property Coverage
|
||||
-----------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate functional coverage points the user has inserted manually in
|
||||
SystemVerilog code through into the Verilated model.
|
||||
translate property coverage points the user has inserted manually in
|
||||
SystemVerilog code into the Verilated model.
|
||||
|
||||
For example, the following SystemVerilog statement will add a coverage
|
||||
point under the coverage name "DefaultClock":
|
||||
For simple coverage points, use the ``cover property`` construct:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
DefaultClock: cover property (@(posedge clk) cyc==3);
|
||||
|
||||
This adds a coverage point that tracks whether the condition has been observed.
|
||||
|
||||
.. _covergroup coverage:
|
||||
|
||||
Covergroup Coverage
|
||||
-------------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate covergroup coverage points the user has inserted manually in
|
||||
SystemVerilog code into the Verilated model. Verilator supports
|
||||
coverpoints with value and transition bins, and cross points.
|
||||
|
||||
|
||||
.. _fsm coverage:
|
||||
|
||||
FSM Coverage
|
||||
|
|
@ -301,7 +314,6 @@ Annotated output produced by :command:`verilator_coverage --annotate` will
|
|||
label FSM points with `fsm_state` and `fsm_arc`, and synthetic fallback
|
||||
transitions with `SYNTHETIC DEFAULT ARC`.
|
||||
|
||||
|
||||
.. _line coverage:
|
||||
|
||||
Line Coverage
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'd
|
|||
VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
|
||||
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
|
||||
// Bin attributes
|
||||
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
|
||||
|
|
@ -52,6 +53,7 @@ VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'd
|
|||
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
|
||||
#define VL_CIK_COLUMN "n"
|
||||
#define VL_CIK_COMMENT "o"
|
||||
#define VL_CIK_CROSS_BINS "Cb"
|
||||
#define VL_CIK_FILENAME "f"
|
||||
#define VL_CIK_FSM_FROM "Ff"
|
||||
#define VL_CIK_FSM_TAG "Fg"
|
||||
|
|
@ -77,6 +79,7 @@ public:
|
|||
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
|
||||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "cross_bins") return VL_CIK_CROSS_BINS;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "fsm_from") return VL_CIK_FSM_FROM;
|
||||
if (key == "fsm_tag") return VL_CIK_FSM_TAG;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ set(HEADERS
|
|||
V3Control.h
|
||||
V3Coverage.h
|
||||
V3CoverageJoin.h
|
||||
V3Covergroup.h
|
||||
V3Dead.h
|
||||
V3DebugBisect.h
|
||||
V3Delayed.h
|
||||
|
|
@ -202,6 +203,7 @@ set(HEADERS
|
|||
V3WidthCommit.h
|
||||
V3WidthRemove.h
|
||||
VlcBucket.h
|
||||
VlcCovergroup.h
|
||||
VlcOptions.h
|
||||
VlcPoint.h
|
||||
VlcSource.h
|
||||
|
|
@ -237,6 +239,7 @@ set(COMMON_SOURCES
|
|||
V3Const__gen.cpp
|
||||
V3Coverage.cpp
|
||||
V3CoverageJoin.cpp
|
||||
V3Covergroup.cpp
|
||||
V3Dead.cpp
|
||||
V3Delayed.cpp
|
||||
V3Depth.cpp
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Coverage.o \
|
||||
V3Covergroup.o \
|
||||
V3CoverageJoin.o \
|
||||
V3Dead.o \
|
||||
V3Delayed.o \
|
||||
|
|
|
|||
139
src/V3Active.cpp
139
src/V3Active.cpp
|
|
@ -638,11 +638,150 @@ public:
|
|||
~ActiveVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Pass 1: collect sample CFuncs and sampling events from covergroup class scopes
|
||||
|
||||
class CovergroupCollectVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// Netlist:
|
||||
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
|
||||
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
|
||||
|
||||
// STATE
|
||||
AstClass* m_classp = nullptr; // Current covergroup class context, or nullptr
|
||||
|
||||
// VISITORS
|
||||
void visit(AstClass* nodep) override {
|
||||
if (!nodep->isCovergroup()) return;
|
||||
VL_RESTORER(m_classp);
|
||||
m_classp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstScope* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void visit(AstCFunc* nodep) override {
|
||||
if (!m_classp) return;
|
||||
if (nodep->isCovergroupSample()) m_classp->user1p(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// V3Covergroup guarantees: only supported-event covergroups survive to V3Active,
|
||||
// and they are always inside a covergroup class (so m_classp is set).
|
||||
// Unlink eventp from cgp so it survives cgp's deletion,
|
||||
// then store it in user2p for use during the second pass.
|
||||
m_classp->user2p(nodep->eventp()->unlinkFrBack());
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CovergroupCollectVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CovergroupCollectVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Pass 2: inject automatic sample() calls for covergroup instances
|
||||
|
||||
class CovergroupInjectVisitor final : public VNVisitor {
|
||||
// NODE STATE (set by CovergroupCollectVisitor, consumed here)
|
||||
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
|
||||
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
|
||||
|
||||
// STATE
|
||||
ActiveNamer m_namer; // Reuse active naming infrastructure
|
||||
|
||||
// VISITORS
|
||||
void visit(AstScope* nodep) override {
|
||||
m_namer.main(nodep); // Initialize active naming for this scope
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstVarScope* nodep) override {
|
||||
// Get the underlying var
|
||||
AstVar* const varp = nodep->varp();
|
||||
UASSERT_OBJ(varp, nodep, "AstVarScope must have non-null varp");
|
||||
|
||||
// Check if the variable is of covergroup class type
|
||||
const AstNodeDType* const dtypep = varp->dtypep();
|
||||
UASSERT_OBJ(dtypep, nodep, "AstVar must have non-null dtypep after V3Width");
|
||||
|
||||
const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType);
|
||||
if (!classRefp) return;
|
||||
|
||||
AstClass* const classp = classRefp->classp();
|
||||
|
||||
// Check if this covergroup has an automatic sampling event
|
||||
AstSenTree* const eventp = VN_CAST(classp->user2p(), SenTree);
|
||||
if (!eventp) return; // No automatic sampling for this covergroup
|
||||
|
||||
// V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc
|
||||
AstCFunc* const sampleCFuncp = VN_AS(classp->user1p(), CFunc);
|
||||
UASSERT_OBJ(sampleCFuncp, nodep,
|
||||
"No sample() CFunc found for covergroup " << classp->name());
|
||||
|
||||
// Create a VarRef to the covergroup instance for the method call
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstVarRef* const varrefp = new AstVarRef{fl, nodep, VAccess::READ};
|
||||
|
||||
// Create the CMethodCall to sample()
|
||||
// Note: We don't pass arguments in argsp since vlSymsp is passed via argTypes
|
||||
AstCMethodCall* const cmethodCallp
|
||||
= new AstCMethodCall{fl, varrefp, sampleCFuncp, nullptr};
|
||||
|
||||
cmethodCallp->dtypeSetVoid();
|
||||
cmethodCallp->argTypes("vlSymsp");
|
||||
|
||||
// Clone the sensitivity for this active block.
|
||||
// V3Scope has already resolved all VarRefs in eventp, so the clone
|
||||
// inherits correct varScopep values with no fixup needed.
|
||||
AstSenTree* senTreep = eventp->cloneTree(false);
|
||||
|
||||
// Get or create the AstActive node for this sensitivity
|
||||
// senTreep is a template used by getActive() which clones it into the AstActive;
|
||||
// delete it afterwards as it is not added to the AST directly.
|
||||
AstActive* const activep = m_namer.getActive(fl, senTreep);
|
||||
VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
|
||||
|
||||
// Wrap the sample() call in an AstAlways so SchedPartition handles it
|
||||
// via visit(AstNodeProcedure*) like any other clocked always block.
|
||||
activep->addStmtsp(
|
||||
new AstAlways{fl, VAlwaysKwd::ALWAYS_FF, nullptr, cmethodCallp->makeStmt()});
|
||||
}
|
||||
|
||||
void visit(AstClass* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Delete the owned sampling event template stored during collection
|
||||
if (AstSenTree* const eventp = VN_CAST(nodep->user2p(), SenTree)) {
|
||||
VL_DO_DANGLING(pushDeletep(eventp), eventp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstActive*) override {} // Don't iterate into actives
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CovergroupInjectVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CovergroupInjectVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
if (v3Global.useCovergroup()) {
|
||||
// Add automatic covergroup sampling in two focused passes.
|
||||
// user1p/user2p on AstClass span both passes; guards must outlive both visitors.
|
||||
const VNUser1InUse user1InUse;
|
||||
const VNUser2InUse user2InUse;
|
||||
CovergroupCollectVisitor{nodep}; // Pass 1: collect CFuncs and events into user#p
|
||||
CovergroupInjectVisitor{nodep}; // Pass 2: inject sample() calls, delete user2p events
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
|
|
|||
114
src/V3AstAttr.h
114
src/V3AstAttr.h
|
|
@ -1149,6 +1149,120 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
|
|||
|
||||
//######################################################################
|
||||
|
||||
class VCoverBinsType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
BINS_ARRAY, // Array of bins with user-speciifed size
|
||||
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
|
||||
BINS_DEFAULT, // Default bin
|
||||
BINS_IGNORE, // Ignore bin
|
||||
BINS_ILLEGAL, // Illegal bin
|
||||
BINS_TRANSITION, // Transition bin
|
||||
BINS_USER, // Single bin with one or more values/ranges
|
||||
BINS_WILDCARD // Wildcard bin
|
||||
};
|
||||
enum en m_e;
|
||||
VCoverBinsType() // LCOV_EXCL_START
|
||||
: m_e{BINS_USER} {} // LCOV_EXCL_STOP
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCoverBinsType(en _e)
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VCoverOptionType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
// Shared by option.* and type_option.*
|
||||
WEIGHT,
|
||||
GOAL,
|
||||
AT_LEAST,
|
||||
AUTO_BIN_MAX,
|
||||
PER_INSTANCE,
|
||||
COMMENT,
|
||||
// option.* only (IEEE 1800-2023 Table 19-1)
|
||||
NAME,
|
||||
CROSS_NUM_PRINT_MISSING,
|
||||
CROSS_RETAIN_AUTO_BINS,
|
||||
DETECT_OVERLAP,
|
||||
GET_INST_COVERAGE,
|
||||
// type_option.* only (IEEE 1800-2023 Table 19-3)
|
||||
STROBE,
|
||||
MERGE_INSTANCES,
|
||||
DISTRIBUTE_FIRST,
|
||||
REAL_INTERVAL,
|
||||
// sentinel - should never appear after parse-time validation
|
||||
UNKNOWN
|
||||
};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCoverOptionType(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"weight",
|
||||
"goal",
|
||||
"at_least",
|
||||
"auto_bin_max",
|
||||
"per_instance",
|
||||
"comment",
|
||||
"name",
|
||||
"cross_num_print_missing",
|
||||
"cross_retain_auto_bins",
|
||||
"detect_overlap",
|
||||
"get_inst_coverage",
|
||||
"strobe",
|
||||
"merge_instances",
|
||||
"distribute_first",
|
||||
"real_interval",
|
||||
"unknown"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VTransRepType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
NONE, // No repetition
|
||||
CONSEC, // Consecutive repetition [*]
|
||||
GOTO, // Goto repetition [->]
|
||||
NONCONS // Nonconsecutive repetition [=]
|
||||
};
|
||||
enum en m_e;
|
||||
VTransRepType() // LCOV_EXCL_START
|
||||
: m_e{NONE} {} // LCOV_EXCL_STOP
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VTransRepType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VTransRepType(int _e) // LCOV_EXCL_START
|
||||
: m_e(static_cast<en>(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* asciiJson() const {
|
||||
static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VDirection final {
|
||||
public:
|
||||
enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF };
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
|||
bool m_verilogTask : 1; // Declared by user as task (versus internal-made)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_isCovergroupSample : 1; // Covergroup sample() method
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
VLifetime m_lifetime; // Default lifetime of local vars
|
||||
VIsCached m_purity; // Pure state
|
||||
|
|
@ -151,7 +152,8 @@ protected:
|
|||
, m_verilogFunction{false}
|
||||
, m_verilogTask{false}
|
||||
, m_virtual{false}
|
||||
, m_needProcess{false} {
|
||||
, m_needProcess{false}
|
||||
, m_isCovergroupSample{false} {
|
||||
addStmtsp(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
|
@ -225,6 +227,8 @@ public:
|
|||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool isCovergroupSample() const { return m_isCovergroupSample; }
|
||||
void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; }
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
|
|
@ -260,6 +264,20 @@ public:
|
|||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode {
|
||||
// Base class for functional coverage items (coverpoints, crosses)
|
||||
protected:
|
||||
string m_name; // Item name
|
||||
|
||||
public:
|
||||
AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstNodeFuncCovItem;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstNodeGen VL_NOT_FINAL : public AstNode {
|
||||
// Generate construct
|
||||
public:
|
||||
|
|
@ -521,6 +539,7 @@ class AstCFunc final : public AstNode {
|
|||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
|
||||
// state
|
||||
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
|
||||
int m_cost; // Function call cost
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||
|
|
@ -551,6 +570,7 @@ public:
|
|||
m_dpiImportWrapper = false;
|
||||
m_recursive = false;
|
||||
m_noLife = false;
|
||||
m_isCovergroupSample = false;
|
||||
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
|
|
@ -626,6 +646,8 @@ public:
|
|||
bool recursive() const { return m_recursive; }
|
||||
void noLife(bool flag) { m_noLife = flag; }
|
||||
bool noLife() const { return m_noLife; }
|
||||
bool isCovergroupSample() const { return m_isCovergroupSample; }
|
||||
void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; }
|
||||
void cost(int cost) { m_cost = cost; }
|
||||
// Special methods
|
||||
bool emptyBody() const {
|
||||
|
|
@ -767,13 +789,16 @@ public:
|
|||
class AstCgOptionAssign final : public AstNode {
|
||||
// A covergroup set of option
|
||||
// Parents: CLASS(covergroup) or cross
|
||||
string m_name; // Option name
|
||||
const VCoverOptionType m_optType; // Option type
|
||||
const string m_name; // Original option name (for diagnostics on unknown options)
|
||||
const bool m_typeOption; // type_option vs option
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
public:
|
||||
AstCgOptionAssign(FileLine* fl, bool typeOption, const string& name, AstNodeExpr* valuep)
|
||||
AstCgOptionAssign(FileLine* fl, bool typeOption, VCoverOptionType optType,
|
||||
const string& rawName, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CgOptionAssign(fl)
|
||||
, m_name{name}
|
||||
, m_optType{optType}
|
||||
, m_name{rawName}
|
||||
, m_typeOption{typeOption} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
|
|
@ -781,7 +806,8 @@ public:
|
|||
// ACCESSORS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VCoverOptionType optionType() const { return m_optType; }
|
||||
bool typeOption() const { return m_typeOption; }
|
||||
};
|
||||
class AstClassExtends final : public AstNode {
|
||||
|
|
@ -1029,6 +1055,145 @@ public:
|
|||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstCoverBin final : public AstNode {
|
||||
// Captures data for a coverpoint 'bins' declaration
|
||||
// @astgen op1 := rangesp : List[AstNode]
|
||||
// @astgen op2 := iffp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
|
||||
// @astgen op4 := transp : List[AstCoverTransSet]
|
||||
const string m_name; // Base name of the bin
|
||||
const VCoverBinsType m_type; // Bin type (eg AUTO, IGNORE, ILLEGAL)
|
||||
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
|
||||
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
|
||||
|
||||
public:
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
|
||||
bool isWildcard = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
: (isIgnore ? VCoverBinsType::BINS_IGNORE
|
||||
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_isWildcard{isWildcard} {
|
||||
addRangesp(rangesp);
|
||||
}
|
||||
// Constructor for automatic bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{VCoverBinsType::BINS_AUTO}
|
||||
, m_isArray{true} {
|
||||
this->arraySizep(arraySizep);
|
||||
}
|
||||
// Constructor for default bins (catch-all)
|
||||
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
// Constructor for transition bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp,
|
||||
VCoverBinsType type = VCoverBinsType::BINS_TRANSITION, bool isArrayBin = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type}
|
||||
, m_isArray{isArrayBin} {
|
||||
UASSERT(transp, "AstCoverBin transition constructor requires non-null transp");
|
||||
addTransp(transp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverBin;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VCoverBinsType binsType() const { return m_type; }
|
||||
bool isWildcard() const { return m_isWildcard; }
|
||||
bool isArray() const { return m_isArray; }
|
||||
void isArray(bool flag) { m_isArray = flag; }
|
||||
};
|
||||
class AstCoverOption final : public AstNode {
|
||||
// Coverage-option assignment
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
const VCoverOptionType m_type; // Option being assigned
|
||||
|
||||
public:
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_type{type} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverOption;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCoverOptionType optionType() const { return m_type; }
|
||||
};
|
||||
class AstCoverTransItem final : public AstNode {
|
||||
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
|
||||
// @astgen op1 := valuesp : List[AstNode]
|
||||
// @astgen op2 := repMinp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := repMaxp : Optional[AstNodeExpr]
|
||||
const VTransRepType m_repType;
|
||||
|
||||
public:
|
||||
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
|
||||
: ASTGEN_SUPER_CoverTransItem(fl)
|
||||
, m_repType{repType} {
|
||||
addValuesp(valuesp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverTransItem;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCoverTransSet final : public AstNode {
|
||||
// Represents a transition set: value1 => value2 => value3
|
||||
// @astgen op1 := itemsp : List[AstCoverTransItem]
|
||||
public:
|
||||
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
|
||||
: ASTGEN_SUPER_CoverTransSet(fl) {
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverTransSet;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCovergroup final : public AstNode {
|
||||
// Represents a covergroup declaration. V3LinkParse transforms this
|
||||
// into an AstClass with isCovergroup==true and attaches the clocking
|
||||
// event as a new AstCovergroup sentinel in class membersp.
|
||||
// @astgen op1 := argsp : List[AstVar]
|
||||
// @astgen op2 := membersp : List[AstNode]
|
||||
// @astgen op3 := eventp : Optional[AstSenTree]
|
||||
// @astgen op4 := sampleArgsp : List[AstVar]
|
||||
string m_name; // covergroup name
|
||||
|
||||
public:
|
||||
AstCovergroup(FileLine* fl, const string& name, AstVar* argsp, AstVar* sampleArgsp,
|
||||
AstNode* membersp, AstSenTree* eventp)
|
||||
: ASTGEN_SUPER_Covergroup(fl)
|
||||
, m_name{name} {
|
||||
addArgsp(argsp);
|
||||
addSampleArgsp(sampleArgsp);
|
||||
addMembersp(membersp);
|
||||
this->eventp(eventp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCovergroup;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstCoverpointRef final : public AstNode {
|
||||
// Reference to a coverpoint used in a cross
|
||||
const string m_name; // coverpoint name
|
||||
|
||||
public:
|
||||
AstCoverpointRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_CoverpointRef(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstCoverpointRef;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
};
|
||||
class AstDefParam final : public AstNode {
|
||||
// A defparam assignment
|
||||
// Parents: MODULE
|
||||
|
|
@ -2602,6 +2767,39 @@ public:
|
|||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
class AstCoverCross final : public AstNodeFuncCovItem {
|
||||
// @astgen op1 := itemsp : List[AstCoverpointRef]
|
||||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||
// // post-LinkParse: empty
|
||||
public:
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
|
||||
: ASTGEN_SUPER_CoverCross(fl, name) {
|
||||
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCross;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCoverpoint final : public AstNodeFuncCovItem {
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := binsp : List[AstNode] // Parse: mixed AstCoverBin/AstCgOptionAssign;
|
||||
// post-LinkParse: AstCoverBin only
|
||||
// @astgen op3 := iffp : Optional[AstNodeExpr]
|
||||
// @astgen op4 := optionsp : List[AstCoverOption]
|
||||
public:
|
||||
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp,
|
||||
AstNodeExpr* iffp = nullptr, AstNode* binsp = nullptr)
|
||||
: ASTGEN_SUPER_Coverpoint(fl, name) {
|
||||
this->exprp(exprp);
|
||||
this->iffp(iffp);
|
||||
addBinsp(binsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverpoint;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
|
||||
// === AstNodeGen ===
|
||||
class AstGenBlock final : public AstNodeGen {
|
||||
|
|
@ -2685,6 +2883,8 @@ class AstClass final : public AstNodeModule {
|
|||
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
|
||||
bool m_virtual = false; // Virtual class
|
||||
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
|
||||
// Covergroup options (when m_covergroup is true)
|
||||
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
|
||||
|
||||
public:
|
||||
AstClass(FileLine* fl, const string& name, const string& libname)
|
||||
|
|
@ -2714,6 +2914,9 @@ public:
|
|||
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
|
||||
void markPrintedFrom() { m_printedFrom = true; }
|
||||
bool isPrintedFrom() const { return m_printedFrom; }
|
||||
// Covergroup options accessors
|
||||
int cgAutoBinMax() const { return m_cgAutoBinMax; }
|
||||
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
|
||||
// Return true if this class is an extension of base class (SLOW)
|
||||
// Accepts nullptrs
|
||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
|
|
|
|||
|
|
@ -3732,3 +3732,54 @@ void AstWith::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Functional coverage dump methods
|
||||
|
||||
void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
|
||||
|
||||
void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
|
||||
|
||||
void AstCoverBin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
if (m_isArray) str << "[]";
|
||||
}
|
||||
|
||||
void AstCoverBin::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
|
||||
if (m_isArray) str << ", \"isArray\": true";
|
||||
}
|
||||
|
||||
void AstCoverTransItem::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (m_repType != VTransRepType::NONE) str << " " << m_repType.ascii();
|
||||
}
|
||||
|
||||
void AstCoverTransItem::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"repType\": " << m_repType.asciiJson();
|
||||
}
|
||||
|
||||
void AstCoverTransSet::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
|
||||
void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
|
||||
|
||||
void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
|
||||
|
||||
void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
|
||||
|
||||
void AstCoverOption::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
}
|
||||
|
||||
void AstCoverOption::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"optionType\": \"" << m_type.ascii() << "\"";
|
||||
}
|
||||
|
||||
void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
|
||||
void AstCoverpointRef::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
|
||||
|
|
|
|||
|
|
@ -288,6 +288,18 @@ class CoverageVisitor final : public VNVisitor {
|
|||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClass* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_state);
|
||||
VL_RESTORER(m_exprTempNames);
|
||||
VL_RESTORER(m_funcTemps);
|
||||
createHandle(nodep);
|
||||
m_modp = nodep;
|
||||
// Covergroup declarations are not executable statements; suppress line/expr/toggle
|
||||
// coverage so declarative elements (covergroup, coverpoint, cross) are not annotated
|
||||
m_state.m_inModOff = nodep->isCovergroup();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAlways* nodep) override {
|
||||
if (nodep->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
||||
// Handle continuous assigns for expression coverage (but not line coverage)
|
||||
|
|
@ -806,7 +818,6 @@ class CoverageVisitor final : public VNVisitor {
|
|||
pair.first->second = varp;
|
||||
if (m_ftaskp) {
|
||||
varp->funcLocal(true);
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
|
||||
} else {
|
||||
m_modp->stmtsp()->addHereThisAsNext(varp);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -307,6 +307,72 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
void visit(AstCoverInc*) override {} // N/A
|
||||
void visit(AstCoverToggle*) override {} // N/A
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// AstCovergroup appears as a member inside the lowered AstClass body.
|
||||
// The outer covergroup/endcovergroup wrapper is already emitted by the
|
||||
// AstNodeModule visitor (verilogKwd()="covergroup" on AstClass::isCovergroup).
|
||||
// Here we only emit the clocking event, if any.
|
||||
if (nodep->eventp()) {
|
||||
putfs(nodep, "");
|
||||
iterateConst(nodep->eventp());
|
||||
}
|
||||
}
|
||||
void visit(AstCoverpoint* nodep) override {
|
||||
putfs(nodep, nodep->name() + ": coverpoint ");
|
||||
iterateAndNextConstNull(nodep->exprp());
|
||||
if (nodep->binsp() || nodep->optionsp()) {
|
||||
puts(" {\n");
|
||||
iterateAndNextConstNull(nodep->optionsp());
|
||||
iterateAndNextConstNull(nodep->binsp());
|
||||
puts("}");
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverBin* nodep) override {
|
||||
switch (nodep->binsType()) {
|
||||
case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break;
|
||||
case VCoverBinsType::BINS_ILLEGAL: putfs(nodep, "illegal_bins "); break;
|
||||
default: putfs(nodep, "bins "); break;
|
||||
}
|
||||
puts(nodep->name());
|
||||
if (nodep->binsType() == VCoverBinsType::BINS_DEFAULT) {
|
||||
puts(" = default");
|
||||
} else if (nodep->transp()) {
|
||||
puts(" = ");
|
||||
for (AstNode* setp = nodep->transp(); setp; setp = setp->nextp()) {
|
||||
if (setp != nodep->transp()) puts(", ");
|
||||
iterateConst(setp);
|
||||
}
|
||||
} else if (nodep->rangesp()) { // LCOV_EXCL_BR_LINE - false: CoverBin always has
|
||||
// transp/rangesp/default
|
||||
puts(" = {");
|
||||
for (AstNode* rangep = nodep->rangesp(); rangep; rangep = rangep->nextp()) {
|
||||
if (rangep != nodep->rangesp()) puts(", ");
|
||||
iterateConst(rangep);
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverpointRef* nodep) override { putfs(nodep, nodep->name()); }
|
||||
void visit(AstCoverCross* nodep) override {
|
||||
putfs(nodep, nodep->name() + ": cross ");
|
||||
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp != nodep->itemsp()) puts(", ");
|
||||
iterateConst(itemp);
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverTransSet* nodep) override {
|
||||
puts("(");
|
||||
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp != nodep->itemsp()) puts(" => ");
|
||||
iterateConst(itemp);
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstCoverTransItem* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
void visit(AstCvtPackString* nodep) override {
|
||||
putfs(nodep, "");
|
||||
if (AstConst* const lhsConstp = VN_CAST(nodep->lhsp(), Const)) {
|
||||
|
|
@ -745,6 +811,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
iterateAndNextConstNull(nodep->elsep());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstInsideRange* nodep) override {
|
||||
puts("[");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(":");
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
puts("]");
|
||||
}
|
||||
void visit(AstRange* nodep) override {
|
||||
puts("[");
|
||||
if (VN_IS(nodep->leftp(), Const) && VN_IS(nodep->rightp(), Const)) {
|
||||
|
|
@ -942,6 +1015,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
|
||||
}
|
||||
}
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
UASSERT_OBJ(nodep->classp(), nodep, "AstClassRefDType not linked");
|
||||
putfs(nodep, EmitCUtil::prefixNameProtect(nodep->classp()));
|
||||
}
|
||||
void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); }
|
||||
void visit(AstModport* nodep) override {
|
||||
puts(nodep->verilogKwd());
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ class V3Global final {
|
|||
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
|
||||
bool m_useParallelBuild = false; // Use parallel build for model
|
||||
bool m_useRandSequence = false; // Has `randsequence`
|
||||
bool m_useCovergroup = false; // Has covergroup declarations
|
||||
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
|
||||
bool m_hasPrintedObjects = false; // Design has format args printed with to_string()
|
||||
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
|
||||
|
|
@ -220,6 +221,8 @@ public:
|
|||
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
||||
bool useRandSequence() const { return m_useRandSequence; }
|
||||
void useRandSequence(bool flag) { m_useRandSequence = flag; }
|
||||
bool useCovergroup() const { return m_useCovergroup; }
|
||||
void useCovergroup(bool flag) { m_useCovergroup = flag; }
|
||||
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
|
||||
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
||||
bool hasPrintedObjects() const { return m_hasPrintedObjects; }
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew
|
||||
AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew
|
||||
int m_anonUdpId = 0; // Counter for anonymous UDP instances
|
||||
int m_coverpointNum = 0; // Counter for unnamed coverpoints within current covergroup
|
||||
int m_genblkAbove = 0; // Begin block number of if/case/for above
|
||||
int m_genblkNum = 0; // Begin block number, 0=none seen
|
||||
int m_beginDepth = 0; // How many begin blocks above current node within current AstNodeModule
|
||||
|
|
@ -1149,6 +1150,268 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Append, for each arg in argsp, an INPUT parameter plus a "this.<member> = <param>"
|
||||
// assignment to funcp. The parameter is a clone of the covergroup member and so shares its
|
||||
// name; 'this.' on the LHS targets the member, otherwise the same-named local parameter
|
||||
// shadows it and the assignment self-assigns the parameter, leaving the member unwritten.
|
||||
// argsp may be null (no args appended).
|
||||
static void addArgMemberCopies(AstFunc* funcp, AstNode* argsp) {
|
||||
for (AstNode* argp = argsp; argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const paramp = origVarp->cloneTree(false);
|
||||
paramp->funcLocal(true);
|
||||
paramp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(paramp);
|
||||
AstNodeExpr* const lhsp = new AstDot{
|
||||
origVarp->fileline(), false, new AstParseRef{origVarp->fileline(), "this"},
|
||||
new AstParseRef{origVarp->fileline(), origVarp->name()}};
|
||||
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
|
||||
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
|
||||
}
|
||||
}
|
||||
|
||||
// Create boilerplate covergroup methods on the given AstClass.
|
||||
// argsp/sampleArgsp are the raw arg lists still owned by the caller; they are iterated
|
||||
// (cloned) but not deleted here.
|
||||
static void createCovergroupMethods(AstClass* nodep, AstFunc* newFuncp, AstNode* argsp,
|
||||
AstNode* sampleArgsp) {
|
||||
// Hidden static to take unspecified reference argument results
|
||||
AstVar* const defaultVarp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
|
||||
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
nodep->addStmtsp(defaultVarp);
|
||||
|
||||
// Handle constructor arguments - add function parameters and assignments
|
||||
if (argsp) {
|
||||
UASSERT_OBJ(newFuncp, nodep,
|
||||
"Covergroup class must have a 'new' constructor function");
|
||||
// Save the existing body statements and unlink them, so the arg assignments run
|
||||
// before the coverage body, then re-append the body.
|
||||
AstNode* const existingBodyp = newFuncp->stmtsp();
|
||||
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
|
||||
addArgMemberCopies(newFuncp, argsp);
|
||||
if (existingBodyp) newFuncp->addStmtsp(existingBodyp);
|
||||
}
|
||||
|
||||
// IEEE: option / type_option members allow external access (cg_inst.option.X)
|
||||
// and require std:: types; std:: is kept because setUsesStdPackage() is called
|
||||
// at parse time for every covergroup declaration.
|
||||
{
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
{
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: function void sample([arguments])
|
||||
{
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
|
||||
addArgMemberCopies(funcp, sampleArgsp);
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: function void start(), void stop()
|
||||
for (const string& name : {"start"s, "stop"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: static function real get_coverage(optional ref int, optional ref int)
|
||||
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
|
||||
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
funcp->isStatic(name == "get_coverage");
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
{
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
|
||||
nodep->findDoubleDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::OUTPUT);
|
||||
varp->funcReturn(true);
|
||||
funcp->fvarp(varp);
|
||||
}
|
||||
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
|
||||
// IEEE: function void set_inst_name(string)
|
||||
{
|
||||
AstFunc* const funcp
|
||||
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// AstCovergroup can only appear inside a module/class/package; never at root level.
|
||||
UASSERT_OBJ(m_modp, nodep, "AstCovergroup not under module");
|
||||
// If we're already inside a covergroup class, this is the sentinel AstCovergroup
|
||||
// node carrying the clocking event for V3Covergroup - don't re-transform it.
|
||||
if (VN_IS(m_modp, Class) && VN_AS(m_modp, Class)->isCovergroup()) return;
|
||||
|
||||
// Transform raw parse-time AstCovergroup into a fully-formed AstClass
|
||||
cleanFileline(nodep);
|
||||
|
||||
const string libname = m_modp->libname();
|
||||
AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname};
|
||||
cgClassp->isCovergroup(true);
|
||||
v3Global.useCovergroup(true);
|
||||
|
||||
// Clocking event: unlink before deleteTree, attach as AstCovergroup child on class
|
||||
if (AstSenTree* const eventp = nodep->eventp()) {
|
||||
eventp->unlinkFrBack();
|
||||
AstCovergroup* const cgNodep = new AstCovergroup{
|
||||
nodep->fileline(), nodep->name(), nullptr, nullptr, nullptr, eventp};
|
||||
cgClassp->addMembersp(cgNodep);
|
||||
}
|
||||
|
||||
// Convert constructor args to member variables
|
||||
for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const memberp = origVarp->cloneTree(false);
|
||||
memberp->varType(VVarType::MEMBER);
|
||||
memberp->funcLocal(false);
|
||||
memberp->direction(VDirection::NONE);
|
||||
cgClassp->addMembersp(memberp);
|
||||
}
|
||||
|
||||
// Convert sample args to member variables
|
||||
for (AstNode* argp = nodep->sampleArgsp(); argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const memberp = origVarp->cloneTree(false);
|
||||
memberp->varType(VVarType::MEMBER);
|
||||
memberp->funcLocal(false);
|
||||
memberp->direction(VDirection::NONE);
|
||||
cgClassp->addMembersp(memberp);
|
||||
}
|
||||
|
||||
// Create the constructor; detach membersp (coverage body) and use as its body
|
||||
AstFunc* const newFuncp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
|
||||
newFuncp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newFuncp->classMethod(true);
|
||||
newFuncp->isConstructor(true);
|
||||
newFuncp->dtypep(cgClassp->dtypep());
|
||||
if (AstNode* const bodyp = nodep->membersp()) {
|
||||
bodyp->unlinkFrBackWithNext();
|
||||
newFuncp->addStmtsp(bodyp);
|
||||
}
|
||||
cgClassp->addMembersp(newFuncp);
|
||||
|
||||
// Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep)
|
||||
createCovergroupMethods(cgClassp, newFuncp, nodep->argsp(), nodep->sampleArgsp());
|
||||
|
||||
// Replace AstCovergroup with AstClass and process the new class normally.
|
||||
// Reset the unnamed-coverpoint counter so synthesized names are stable and
|
||||
// independent of unrelated covergroups elsewhere in the file.
|
||||
VL_RESTORER(m_coverpointNum);
|
||||
m_coverpointNum = 0;
|
||||
nodep->replaceWith(cgClassp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(cgClassp);
|
||||
}
|
||||
|
||||
void visit(AstCoverpoint* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
// Give every coverpoint a guaranteed-unique, deterministic name so all downstream
|
||||
// consumers (generated bin-variable names, the cross coverpoint map, hierarchical
|
||||
// report names) see a consistent identifier. Unlabeled coverpoints arrive from the
|
||||
// grammar with an empty name; left empty, two of them in one covergroup collide on
|
||||
// the generated "__Vcov__<bin>" variable name (e.g. duplicate "__Vcov__auto_0").
|
||||
if (nodep->name().empty()) {
|
||||
// A single-identifier coverpoint expression is the only form that parses to an
|
||||
// AstParseRef with a usable name here; a dotted/scoped/select/concatenation/call
|
||||
// expression is either a different node (so the cast yields null) or a name-less
|
||||
// AstParseRef (e.g. a member-select). Either of those gets a synthesized name.
|
||||
const AstParseRef* const refp = VN_CAST(nodep->exprp(), ParseRef);
|
||||
if (refp && !refp->name().empty()) {
|
||||
// Single-identifier coverpoint: take the variable's name (IEEE 1800-2023
|
||||
// 19.5 - an unlabeled coverpoint of a single variable is named for it).
|
||||
nodep->name(refp->name());
|
||||
} else {
|
||||
// Compound expression (member/part select, concatenation, ...): synthesize
|
||||
// a unique name. Leading "__V" keeps it out of the user namespace.
|
||||
nodep->name("__Vcoverpoint" + cvtToStr(m_coverpointNum++));
|
||||
}
|
||||
}
|
||||
// Re-sort the parse-time mixed bins list (AstCoverBin + AstCgOptionAssign)
|
||||
// into the typed binsp and optionsp slots. The grammar attaches both node types
|
||||
// to binsp (op2) as a raw List[AstNode]; now that they are properly parented we
|
||||
// can iterate and split them without any temporary-parent tricks.
|
||||
for (AstNode *itemp = nodep->binsp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = itemp->nextp();
|
||||
if (AstCgOptionAssign* const optp = VN_CAST(itemp, CgOptionAssign)) {
|
||||
optp->unlinkFrBack();
|
||||
if (optp->optionType() == VCoverOptionType::AT_LEAST
|
||||
|| optp->optionType() == VCoverOptionType::AUTO_BIN_MAX) {
|
||||
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optionType(),
|
||||
optp->valuep()->cloneTree(false)});
|
||||
} else {
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage option: " + optp->prettyNameQ());
|
||||
}
|
||||
VL_DO_DANGLING(optp->deleteTree(), optp);
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCoverCross* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
// Distribute the parse-time raw cross_body list (rawBodyp, op3) into the
|
||||
// typed optionsp slot. The grammar produces AstCgOptionAssign nodes for
|
||||
// option.* items; convert them to AstCoverOption exactly as visit(AstCoverpoint*)
|
||||
// does. Other items (functions, unsupported bin selectors) are discarded.
|
||||
for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = itemp->nextp();
|
||||
itemp->unlinkFrBack();
|
||||
AstCgOptionAssign* const optp = VN_AS(itemp, CgOptionAssign);
|
||||
const VCoverOptionType optType = optp->optionType();
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage cross option: " + optp->prettyNameQ());
|
||||
// Always preserve the option node so V3Coverage can track its source line
|
||||
// for coverage annotation, even when the option itself is unsupported.
|
||||
nodep->addOptionsp(
|
||||
new AstCoverOption{optp->fileline(), optType, optp->valuep()->cloneTree(false)});
|
||||
VL_DO_DANGLING(optp->deleteTree(), optp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
// Default: Just iterate
|
||||
cleanFileline(nodep);
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
//*************************************************************************
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Control.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3ParseImp.h" // Defines YYTYPE; before including bison header
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
class V3ParseGrammar final {
|
||||
public:
|
||||
|
|
@ -95,97 +97,6 @@ public:
|
|||
nodep->trace(singletonp()->allTracingOn(fileline));
|
||||
return nodep;
|
||||
}
|
||||
void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) {
|
||||
// Hidden static to take unspecified reference argument results
|
||||
AstVar* const defaultVarp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
|
||||
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
nodep->addStmtsp(defaultVarp);
|
||||
|
||||
// IEEE: option
|
||||
{
|
||||
v3Global.setUsesStdPackage();
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: type_option
|
||||
{
|
||||
v3Global.setUsesStdPackage();
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: function void sample()
|
||||
{
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
|
||||
funcp->addStmtsp(sampleArgs);
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: function void start(), void stop()
|
||||
for (const string& name : {"start"s, "stop"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: static function real get_coverage(optional ref int, optional ref int)
|
||||
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
|
||||
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
funcp->isStatic(name == "get_coverage");
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
{
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
|
||||
nodep->findDoubleDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::OUTPUT);
|
||||
varp->funcReturn(true);
|
||||
funcp->fvarp(varp);
|
||||
}
|
||||
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
// IEEE: function void set_inst_name(string)
|
||||
{
|
||||
AstFunc* const funcp
|
||||
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
AstDisplay* createDisplayError(FileLine* fileline) {
|
||||
AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr};
|
||||
AstNode::addNext<AstNode, AstNode>(nodep, new AstStop{fileline, false});
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,7 @@ class TaskVisitor final : public VNVisitor {
|
|||
cfuncp->isVirtual(nodep->isVirtual());
|
||||
cfuncp->dpiPure(nodep->dpiPure());
|
||||
if (nodep->name() == "new") cfuncp->isConstructor(true);
|
||||
if (nodep->isCovergroupSample()) cfuncp->isCovergroupSample(true);
|
||||
if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
|
||||
|
||||
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());
|
||||
|
|
|
|||
|
|
@ -343,11 +343,15 @@ class TimingSuspendableVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()), getSuspendDepVtx(m_procp),
|
||||
P_CALL};
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
UASSERT_OBJ(funcp, nodep, "AstNodeCCall must have non-null funcp post-link");
|
||||
UASSERT_OBJ(m_procp, nodep, "AstNodeCCall must be inside a procedure/CFunc/Begin");
|
||||
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||
UINFO(9, "V3Timing: Processing CCall to " << funcp->name() << " in dependency graph\n");
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(funcp), getSuspendDepVtx(m_procp), P_CALL};
|
||||
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(funcp), getNeedsProcDepVtx(m_procp),
|
||||
P_CALL};
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -976,8 +980,16 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
if (nodep->funcp()->needProcess()) m_hasProcess = true;
|
||||
if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
|
||||
// Skip automatic covergroup sampling calls
|
||||
if (funcp->isCovergroupSample()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
if (funcp->needProcess()) m_hasProcess = true;
|
||||
if (hasFlags(funcp, T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
// Calls to suspendables are always void return type, hence parent must be StmtExpr
|
||||
AstStmtExpr* const stmtp = VN_AS(nodep->backp(), StmtExpr);
|
||||
stmtp->replaceWith(new AstCAwait{nodep->fileline(), nodep->unlinkFrBack()});
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstClass* m_cgClassp = nullptr; // Current covergroup class
|
||||
AstNodeModule* m_modep = nullptr; // Current module
|
||||
const AstConstraint* m_constraintp = nullptr; // Current constraint
|
||||
AstNodeProcedure* m_procedurep = nullptr; // Current final/always
|
||||
|
|
@ -1896,10 +1897,35 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
|
||||
}
|
||||
void visit(AstCgOptionAssign* nodep) override {
|
||||
// We report COVERIGN on the whole covergroup; if get more fine-grained add this
|
||||
// nodep->v3warn(COVERIGN, "Ignoring unsupported: coverage option");
|
||||
// Extract covergroup option values and store in AstClass before deleting.
|
||||
// m_cgClassp is always set here: AstCgOptionAssign only appears in covergroup
|
||||
// class bodies, and visitClass sets m_cgClassp before iterating children.
|
||||
if (nodep->optionType() == VCoverOptionType::AUTO_BIN_MAX) {
|
||||
// By V3Width time, V3Param has already folded any parameter references.
|
||||
// If the value is still not a constant, it is a runtime expression - emit error.
|
||||
if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) {
|
||||
m_cgClassp->cgAutoBinMax(constp->toSInt());
|
||||
UINFO(6, " Covergroup " << m_cgClassp->name()
|
||||
<< " option.auto_bin_max = " << constp->toSInt() << endl);
|
||||
} else {
|
||||
nodep->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant "
|
||||
"'option.auto_bin_max'; using default value");
|
||||
}
|
||||
}
|
||||
// Add more options here as needed (weight, goal, at_least, per_instance, comment)
|
||||
|
||||
// Delete the assignment node (we've extracted the value)
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstCoverpoint* nodep) override {
|
||||
// The coverpoint expression is self-determined (IEEE 1800-2023 19.5). Width it
|
||||
// with a context so a bit/part-select (AstSel) is sized here; otherwise it would
|
||||
// reach assertAtExpr() with m_vup==null and fail as an internal error.
|
||||
userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
|
||||
userIterateAndNext(nodep->binsp(), nullptr);
|
||||
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
|
||||
userIterateAndNext(nodep->optionsp(), nullptr);
|
||||
}
|
||||
void visit(AstPow* nodep) override {
|
||||
// Pow is special, output sign only depends on LHS sign, but
|
||||
// function result depends on both signs
|
||||
|
|
@ -3539,7 +3565,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
return AstEqWild::newTyped(itemp->fileline(), exprp, itemp->unlinkFrBack());
|
||||
}
|
||||
void visit(AstInsideRange* nodep) override {
|
||||
// Just do each side; AstInside will rip these nodes out later
|
||||
// Just do each side; AstInside will rip these nodes out later.
|
||||
// When m_vup is null, this range appears outside a normal expression context (e.g.
|
||||
// in a covergroup bin declaration). Pre-fold constant arithmetic in that case
|
||||
// (e.g., AstNegate(Const) -> Const) so children have their types set before widthing.
|
||||
// We cannot do this unconditionally: in a normal 'inside' expression (m_vup set),
|
||||
// range bounds may be enum refs not yet widthed, and constifyEdit would crash.
|
||||
if (!m_vup) {
|
||||
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
||||
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
||||
}
|
||||
userIterateAndNext(nodep->lhsp(), m_vup);
|
||||
userIterateAndNext(nodep->rhsp(), m_vup);
|
||||
nodep->dtypeFrom(nodep->lhsp());
|
||||
|
|
@ -7712,6 +7747,8 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Must do extends first, as we may in functions under this class
|
||||
// start following a tree of extends that takes us to other classes
|
||||
userIterateAndNext(nodep->extendsp(), nullptr);
|
||||
VL_RESTORER(m_cgClassp);
|
||||
if (nodep->isCovergroup()) m_cgClassp = nodep;
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "V3Control.h"
|
||||
#include "V3Coverage.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
#include "V3Covergroup.h"
|
||||
#include "V3Dead.h"
|
||||
#include "V3Delayed.h"
|
||||
#include "V3Depth.h"
|
||||
|
|
@ -240,6 +241,10 @@ static void process() {
|
|||
// the AST context needed to recover and lower FSMs reliably.
|
||||
if (v3Global.opt.coverageNonFsm()) V3Coverage::coverage(v3Global.rootp());
|
||||
|
||||
// Functional coverage code generation
|
||||
// Generate code for covergroups/coverpoints
|
||||
if (v3Global.useCovergroup()) V3Covergroup::covergroup(v3Global.rootp());
|
||||
|
||||
// Resolve randsequence if they are used by the design
|
||||
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ public:
|
|||
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
|
||||
string page() const { return keyExtract("page", m_name.c_str()); }
|
||||
string type() const { return typeExtract(m_name.c_str()); }
|
||||
// Covergroup-specific key accessors (long keys, no short-key alias)
|
||||
string bin() const { return keyExtract("bin", m_name.c_str()); }
|
||||
string binType() const { return keyExtract("bin_type", m_name.c_str()); }
|
||||
bool isCross() const { return !keyExtract("cross", m_name.c_str()).empty(); }
|
||||
string crossBins() const { return keyExtract(VL_CIK_CROSS_BINS, m_name.c_str()); }
|
||||
string thresh() const {
|
||||
// string as maybe ""
|
||||
return keyExtract(VL_CIK_THRESH, m_name.c_str());
|
||||
|
|
@ -123,6 +128,23 @@ public:
|
|||
os << std::setw(6) << std::setfill('0') << count();
|
||||
os << " point: type=" << type() << " comment=" << comment() << " hier=" << hier();
|
||||
os << "\n";
|
||||
if (isCross()) {
|
||||
const string bins = crossBins();
|
||||
if (!bins.empty()) {
|
||||
os << " // cross: [";
|
||||
bool first = true;
|
||||
size_t start = 0;
|
||||
while (true) {
|
||||
const size_t comma = bins.find(',', start);
|
||||
if (!first) os << ", ";
|
||||
os << bins.substr(start, comma == string::npos ? string::npos : comma - start);
|
||||
first = false;
|
||||
if (comma == string::npos) break;
|
||||
start = comma + 1;
|
||||
}
|
||||
os << "]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -346,8 +348,6 @@ void VlcTop::rank() {
|
|||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
void VlcTop::annotateCalc() {
|
||||
// Calculate per-line information into filedata structure
|
||||
for (const auto& i : m_points) {
|
||||
|
|
|
|||
369
src/verilog.y
369
src/verilog.y
|
|
@ -3958,16 +3958,16 @@ value_range<nodeExprp>: // ==IEEE: value_range/open_value_range
|
|||
covergroup_value_range<nodeExprp>: // ==IEEE-2012: covergroup_value_range
|
||||
cgexpr { $$ = $1; }
|
||||
| '[' cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
// // IEEE-2023: added all four:
|
||||
// // Skipped as '$' is part of our expr
|
||||
// // IEEE-2023: '[' '$' ':' cgexpr ']'
|
||||
// // Skipped as '$' is part of our expr
|
||||
// // IEEE-2023: '[' cgexpr ':' '$' ']'
|
||||
| '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
| '[' cgexpr yP_PLUSPCTMINUS cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
;
|
||||
|
||||
caseCondList<nodeExprp>: // IEEE: part of case_item
|
||||
|
|
@ -6970,40 +6970,27 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
|
|||
yCOVERGROUP idAny cgPortListE coverage_eventE ';'
|
||||
/*cont*/ coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ AstClass *cgClassp = new AstClass{$<fl>2, *$2, PARSEP->libname()};
|
||||
cgClassp->isCovergroup(true);
|
||||
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
|
||||
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newp->classMethod(true);
|
||||
newp->isConstructor(true);
|
||||
newp->dtypep(cgClassp->dtypep());
|
||||
newp->addStmtsp($3);
|
||||
newp->addStmtsp($6);
|
||||
cgClassp->addMembersp(newp);
|
||||
GRAMMARP->createCoverGroupMethods(cgClassp, $4);
|
||||
|
||||
$$ = cgClassp;
|
||||
GRAMMARP->endLabel($<fl>8, $$, $8);
|
||||
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
|
||||
}
|
||||
{ AstSenTree* clockp = nullptr;
|
||||
AstNode* sampleArgsp = nullptr;
|
||||
if ($4) {
|
||||
if (VN_IS($4, SenItem)) {
|
||||
clockp = new AstSenTree{$<fl>1, VN_AS($4, SenItem)};
|
||||
} else {
|
||||
sampleArgsp = $4;
|
||||
}
|
||||
}
|
||||
$$ = new AstCovergroup{$<fl>1, *$2, static_cast<AstVar*>($3),
|
||||
static_cast<AstVar*>(sampleArgsp), $6, clockp};
|
||||
// Every covergroup has option/type_option members (added by V3LinkParse)
|
||||
// referencing std:: types, so mark std as needed at parse time.
|
||||
v3Global.setUsesStdPackage();
|
||||
GRAMMARP->endLabel($<fl>8, $$, $8); }
|
||||
| yCOVERGROUP yEXTENDS idAny ';'
|
||||
/*cont*/ coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ AstClass *cgClassp = new AstClass{$<fl>3, *$3, PARSEP->libname()};
|
||||
cgClassp->isCovergroup(true);
|
||||
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
|
||||
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newp->classMethod(true);
|
||||
newp->isConstructor(true);
|
||||
newp->dtypep(cgClassp->dtypep());
|
||||
newp->addStmtsp($5);
|
||||
cgClassp->addMembersp(newp);
|
||||
GRAMMARP->createCoverGroupMethods(cgClassp, nullptr);
|
||||
|
||||
$$ = cgClassp;
|
||||
GRAMMARP->endLabel($<fl>7, $$, $7);
|
||||
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
|
||||
}
|
||||
{ $$ = nullptr;
|
||||
BBUNSUP($1, "Unsupported: covergroup inheritance (extends)");
|
||||
DEL($5); }
|
||||
;
|
||||
|
||||
cgPortListE<nodep>:
|
||||
|
|
@ -7036,12 +7023,61 @@ coverage_spec_or_option<nodep>: // ==IEEE: coverage_spec_or_option
|
|||
coverage_option<nodep>: // ==IEEE: coverage_option
|
||||
// // option/type_option aren't really keywords
|
||||
id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr
|
||||
{ if (*$1 == "option") {
|
||||
$$ = new AstCgOptionAssign{$<fl>1, false, *$3, $5};
|
||||
} else if (*$1 == "type_option") {
|
||||
$$ = new AstCgOptionAssign{$<fl>1, true, *$3, $5};
|
||||
{ $$ = nullptr;
|
||||
if (*$1 == "option" || *$1 == "type_option") {
|
||||
const bool typeOpt = (*$1 == "type_option");
|
||||
VCoverOptionType optType = VCoverOptionType::UNKNOWN;
|
||||
bool valid = true;
|
||||
if (!typeOpt) {
|
||||
// IEEE 1800-2023 Table 19-1: option.* names
|
||||
if (*$3 == "at_least") optType = VCoverOptionType::AT_LEAST;
|
||||
else if (*$3 == "auto_bin_max") optType = VCoverOptionType::AUTO_BIN_MAX;
|
||||
else if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
|
||||
else if (*$3 == "cross_num_print_missing") optType = VCoverOptionType::CROSS_NUM_PRINT_MISSING;
|
||||
else if (*$3 == "cross_retain_auto_bins") optType = VCoverOptionType::CROSS_RETAIN_AUTO_BINS;
|
||||
else if (*$3 == "detect_overlap") optType = VCoverOptionType::DETECT_OVERLAP;
|
||||
else if (*$3 == "get_inst_coverage") optType = VCoverOptionType::GET_INST_COVERAGE;
|
||||
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
|
||||
else if (*$3 == "name") optType = VCoverOptionType::NAME;
|
||||
else if (*$3 == "per_instance") optType = VCoverOptionType::PER_INSTANCE;
|
||||
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
|
||||
else {
|
||||
$<fl>1->v3error("Unknown coverage option name 'option."
|
||||
<< *$3 << "'"
|
||||
<< "; not a valid option per IEEE 1800-2023 Table 19-1");
|
||||
valid = false;
|
||||
}
|
||||
} else {
|
||||
// IEEE 1800-2023 Table 19-3: type_option.* names
|
||||
if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
|
||||
else if (*$3 == "distribute_first") optType = VCoverOptionType::DISTRIBUTE_FIRST;
|
||||
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
|
||||
else if (*$3 == "merge_instances") optType = VCoverOptionType::MERGE_INSTANCES;
|
||||
else if (*$3 == "real_interval") optType = VCoverOptionType::REAL_INTERVAL;
|
||||
else if (*$3 == "strobe") optType = VCoverOptionType::STROBE;
|
||||
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
|
||||
else {
|
||||
// Specific message for option.* names used under type_option.*
|
||||
if (*$3 == "at_least" || *$3 == "auto_bin_max"
|
||||
|| *$3 == "cross_num_print_missing" || *$3 == "cross_retain_auto_bins"
|
||||
|| *$3 == "detect_overlap" || *$3 == "get_inst_coverage"
|
||||
|| *$3 == "name" || *$3 == "per_instance") {
|
||||
$<fl>1->v3error("'type_option." << *$3
|
||||
<< "' is not valid; use 'option." << *$3 << "' instead");
|
||||
} else {
|
||||
$<fl>1->v3error("Unknown coverage type option name 'type_option."
|
||||
<< *$3 << "'"
|
||||
<< "; not a valid type option per IEEE 1800-2023 Table 19-3");
|
||||
}
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
$$ = new AstCgOptionAssign{$<fl>1, typeOpt, optType, *$3, $5};
|
||||
} else {
|
||||
DEL($5);
|
||||
}
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
$<fl>1->v3error("Syntax error; expected 'option' or 'type_option': '" << *$1 << "'");
|
||||
DEL($5);
|
||||
} }
|
||||
|
|
@ -7050,29 +7086,33 @@ coverage_option<nodep>: // ==IEEE: coverage_option
|
|||
cover_point<nodep>: // ==IEEE: cover_point
|
||||
// // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT
|
||||
yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverpoint"); DEL($2, $3, $4); }
|
||||
{ $$ = new AstCoverpoint{$<fl>1, "", $2, $3, $4}; }
|
||||
// // IEEE-2012: class_scope before an ID
|
||||
| id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: coverpoint"); DEL($4, $5, $6);}
|
||||
{ $$ = new AstCoverpoint{$<fl>3, *$1, $4, $5, $6}; }
|
||||
// // data_type_or_implicit expansion
|
||||
| data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($1, $5, $6, $7);}
|
||||
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7};
|
||||
DEL($1); }
|
||||
| yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); }
|
||||
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7}; }
|
||||
// // IEEE-2012:
|
||||
| bins_or_empty { $$ = $1; }
|
||||
;
|
||||
|
||||
iffE<nodep>: // IEEE: part of cover_point, others
|
||||
iffE<nodeExprp>: // IEEE: part of cover_point, others
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| yIFF '(' expr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover 'iff'"); DEL($3); }
|
||||
{ $$ = $3; /* Keep iff condition for coverpoint */ }
|
||||
;
|
||||
|
||||
bins_or_empty<nodep>: // ==IEEE: bins_or_empty
|
||||
|
|
@ -7096,39 +7136,104 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
|
|||
// // Superset of IEEE - we allow []'s in more places
|
||||
coverage_option { $$ = $1; }
|
||||
// // Can't use wildcardE as results in conflicts
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin specification"); DEL($3, $6, $8); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>8, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $6, $10, $12); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $8, $10); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'wildcard' specification"); DEL($4, $7, $9); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($4, $7, $11, $13); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE
|
||||
{ // Check for automatic bins: bins auto[N]
|
||||
if (*$2 == "auto") {
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, $4};
|
||||
DEL($6);
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
BBCOVERIGN($<fl>2, "Unsupported: 'bins' array (non-auto)");
|
||||
DEL($4, $6);
|
||||
}
|
||||
}
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
//
|
||||
// // cgexpr part of trans_list
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin trans list"); DEL($3, $5, $6); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($4, $6, $7);}
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_TRANSITION}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_IGNORE}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_ILLEGAL}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
//
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'default'"); DEL($3, $6); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($3, $7); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_DEFAULT};
|
||||
DEL($6); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_IGNORE};
|
||||
DEL($6); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_ILLEGAL};
|
||||
DEL($6); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
;
|
||||
|
||||
bins_orBraE<nodep>: // IEEE: part of bins_or_options:
|
||||
bins_orBraE<fl>: // IEEE: part of bins_or_options: returns fileline (abuse for boolean flag)
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| '[' ']' { $$ = nullptr; /*UNSUP*/ }
|
||||
| '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); }
|
||||
;
|
||||
|
||||
bins_keyword<fl>: // ==IEEE: bins_keyword
|
||||
yBINS { $$ = $1; /*UNSUP*/ }
|
||||
| yILLEGAL_BINS { $$ = $1; /*UNSUP*/ }
|
||||
| yIGNORE_BINS { $$ = $1; /*UNSUP*/ }
|
||||
| '[' ']' { $$ = $<fl>1; /* Mark as array */ }
|
||||
| '[' cgexpr ']' { BBCOVERIGN($<fl>1, "Unsupported: 'bins' explicit array size (treated as '[]')"); DEL($2); $$ = $<fl>1; }
|
||||
;
|
||||
|
||||
trans_list<nodep>: // ==IEEE: trans_list
|
||||
|
|
@ -7136,30 +7241,42 @@ trans_list<nodep>: // ==IEEE: trans_list
|
|||
| trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); }
|
||||
;
|
||||
|
||||
trans_set<nodep>: // ==IEEE: trans_set
|
||||
trans_range_list { $$ = $1; }
|
||||
// // Note the { => } in the grammar, this is really a list
|
||||
trans_set<nodep>: // ==IEEE: trans_set (returns AstCoverTransSet)
|
||||
trans_range_list {
|
||||
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($1);
|
||||
$$ = new AstCoverTransSet{$<fl>1, itemp};
|
||||
}
|
||||
| trans_set yP_EQGT trans_range_list
|
||||
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); }
|
||||
{
|
||||
AstCoverTransSet* const setp = static_cast<AstCoverTransSet*>($1);
|
||||
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($3);
|
||||
setp->addItemsp(itemp);
|
||||
$$ = setp;
|
||||
}
|
||||
;
|
||||
|
||||
trans_range_list<nodep>: // ==IEEE: trans_range_list
|
||||
trans_item { $$ = $1; }
|
||||
trans_range_list<nodep>: // ==IEEE: trans_range_list (returns AstCoverTransItem)
|
||||
trans_item {
|
||||
// Simple transition item without repetition
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONE};
|
||||
}
|
||||
| trans_item yP_BRASTAR cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3); }
|
||||
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3, $5); }
|
||||
| trans_item yP_BRAMINUSGT cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3); }
|
||||
{ BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($3);
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::GOTO}; }
|
||||
| trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($1, $3, $5); }
|
||||
| trans_item yP_BRAEQ cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3); }
|
||||
{ BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($3);
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONCONS}; }
|
||||
| trans_item yP_BRAEQ cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($1, $3, $5); }
|
||||
;
|
||||
|
||||
trans_item<nodep>: // ==IEEE: range_list
|
||||
trans_item<nodep>: // ==IEEE: range_list (returns range list node)
|
||||
covergroup_range_list { $$ = $1; }
|
||||
;
|
||||
|
||||
|
|
@ -7171,9 +7288,28 @@ covergroup_range_list<nodep>: // ==IEEE: covergroup_range_list
|
|||
|
||||
cover_cross<nodep>: // ==IEEE: cover_cross
|
||||
id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: cover cross"); DEL($4, $5, $6); }
|
||||
{
|
||||
AstCoverCross* const nodep = new AstCoverCross{$<fl>3, *$1,
|
||||
VN_AS($4, CoverpointRef)};
|
||||
if ($6) nodep->addRawBodyp($6);
|
||||
if ($5) {
|
||||
$5->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
|
||||
VL_DO_DANGLING($5->deleteTree(), $5);
|
||||
}
|
||||
$$ = nodep;
|
||||
}
|
||||
| yCROSS list_of_cross_items iffE cross_body
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover cross"); DEL($2, $3, $4); }
|
||||
{
|
||||
AstCoverCross* const nodep = new AstCoverCross{$<fl>1,
|
||||
"__cross" + cvtToStr(GRAMMARP->s_typeImpNum++),
|
||||
VN_AS($2, CoverpointRef)};
|
||||
if ($4) nodep->addRawBodyp($4);
|
||||
if ($3) {
|
||||
$3->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
|
||||
VL_DO_DANGLING($3->deleteTree(), $3);
|
||||
}
|
||||
$$ = nodep;
|
||||
}
|
||||
;
|
||||
|
||||
list_of_cross_items<nodep>: // ==IEEE: list_of_cross_items
|
||||
|
|
@ -7188,7 +7324,8 @@ cross_itemList<nodep>: // IEEE: part of list_of_cross_items
|
|||
;
|
||||
|
||||
cross_item<nodep>: // ==IEEE: cross_item
|
||||
idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ }
|
||||
id/*cover_point_identifier*/
|
||||
{ $$ = new AstCoverpointRef{$<fl>1, *$1}; }
|
||||
;
|
||||
|
||||
cross_body<nodep>: // ==IEEE: cross_body
|
||||
|
|
@ -7208,12 +7345,16 @@ cross_body_itemList<nodep>: // IEEE: part of cross_body
|
|||
|
||||
cross_body_item<nodep>: // ==IEEE: cross_body_item
|
||||
function_declaration
|
||||
{ $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1->fileline(), "Unsupported: 'function' in coverage cross body"); DEL($1); }
|
||||
// // IEEE: bins_selection_or_option
|
||||
| coverage_option ';' { $$ = $1; }
|
||||
// // IEEE: bins_selection
|
||||
| bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); DEL($4, $5); }
|
||||
// // IEEE: bins_selection - for now, we ignore explicit cross bins
|
||||
| yBINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| yIGNORE_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| yILLEGAL_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| error ';' { $$ = nullptr; } // LCOV_EXCL_LINE
|
||||
;
|
||||
|
||||
|
|
@ -7221,28 +7362,28 @@ select_expression<nodep>: // ==IEEE: select_expression
|
|||
select_expression_r
|
||||
{ $$ = $1; }
|
||||
| select_expression yP_ANDAND select_expression
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '&&' in coverage select expression"); DEL($1, $3); }
|
||||
| select_expression yP_OROR select_expression
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '||' in coverage select expression"); DEL($1, $3); }
|
||||
;
|
||||
|
||||
// This non-terminal exists to disambiguate select_expression and make "with" bind tighter
|
||||
select_expression_r<nodep>:
|
||||
// // IEEE: select_condition expanded here
|
||||
yBINSOF '(' bins_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($3); }
|
||||
| '!' yBINSOF '(' bins_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($4); }
|
||||
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($3, $7); }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($7); }
|
||||
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($4, $8); }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($4, $8); }
|
||||
| yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($3); }
|
||||
| '!' yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($4); }
|
||||
| select_expression_r yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression with"); DEL($1, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: 'with' in coverage select expression"); DEL($1, $4); }
|
||||
// // IEEE-2012: Need clarification as to precedence
|
||||
//UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { }
|
||||
// // IEEE-2012: Need clarification as to precedence
|
||||
|
|
@ -7260,7 +7401,7 @@ select_expression_r<nodep>:
|
|||
//UNSUP cgexpr yMATCHES cgexpr {..}
|
||||
//UNSUP // Below are all removed
|
||||
| idAny '(' list_of_argumentsE ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage select function call"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: function call in coverage select expression"); DEL($3); }
|
||||
//UNSUP // Above are all removed, replace with:
|
||||
;
|
||||
|
||||
|
|
@ -7279,7 +7420,7 @@ bins_expression<nodep>: // ==IEEE: bins_expression
|
|||
coverage_eventE<nodep>: // IEEE: [ coverage_event ]
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| clocking_event
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage clocking event"); DEL($1); }
|
||||
{ $$ = $1; } // Keep the clocking event for automatic sampling
|
||||
| yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')'
|
||||
{ if (*$3 != "sample") {
|
||||
$<fl>3->v3error("Coverage sampling function must be named 'sample'");
|
||||
|
|
@ -7290,7 +7431,7 @@ coverage_eventE<nodep>: // IEEE: [ coverage_event ]
|
|||
}
|
||||
}
|
||||
| yP_ATAT '(' block_event_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage '@@' events"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: '@@' coverage event"); DEL($3); }
|
||||
;
|
||||
|
||||
block_event_expression<nodep>: // ==IEEE: block_event_expression
|
||||
|
|
@ -7788,11 +7929,15 @@ class_item<nodep>: // ==IEEE: class_item
|
|||
| timeunits_declaration { $$ = $1; }
|
||||
| covergroup_declaration
|
||||
{
|
||||
const string cgName = $1->name();
|
||||
$1->name("__vlAnonCG_" + cgName);
|
||||
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
|
||||
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
|
||||
$$ = addNextNull($1, newp);
|
||||
if ($1) {
|
||||
const string cgName = $1->name();
|
||||
$1->name("__vlAnonCG_" + cgName);
|
||||
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
|
||||
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
|
||||
$$ = addNextNull($1, newp);
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
}
|
||||
}
|
||||
// // local_parameter_declaration under parameter_declaration
|
||||
| parameter_declaration ';' { $$ = $1; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def covergroup_coverage_report(test, outfile=None):
|
||||
"""Parse coverage.dat and write a sorted covergroup bin hit-count report.
|
||||
|
||||
Lines have the form: <hierarchy>[ [bin_type]]: <count>
|
||||
ignore_bins and illegal_bins are annotated with [ignore] / [illegal].
|
||||
|
||||
Returns the path to the written report file.
|
||||
"""
|
||||
if outfile is None:
|
||||
outfile = test.obj_dir + "/covergroup_report.txt"
|
||||
contents = test.file_contents(test.coverage_filename)
|
||||
entries = []
|
||||
for m in re.finditer(r"C '([^']+)' (\d+)", contents):
|
||||
entry, count = m.group(1), m.group(2)
|
||||
if '\x01t\x02covergroup' not in entry:
|
||||
continue
|
||||
h_m = re.search(r'\x01h\x02([^\x01]+)', entry)
|
||||
if not h_m:
|
||||
continue
|
||||
hier = h_m.group(1)
|
||||
bt_m = re.search(r'\x01bin_type\x02([^\x01]+)', entry)
|
||||
cross_m = re.search(r'\x01cross\x021', entry)
|
||||
annotations = []
|
||||
if bt_m:
|
||||
annotations.append(bt_m.group(1))
|
||||
if cross_m:
|
||||
annotations.append("cross")
|
||||
label = f"{hier} [{','.join(annotations)}]" if annotations else hier
|
||||
entries.append((hier, label, int(count)))
|
||||
entries.sort()
|
||||
with open(outfile, 'w', encoding='utf-8') as fh:
|
||||
for _hier, label, count in entries:
|
||||
fh.write(f"{label}: {count}\n")
|
||||
return outfile
|
||||
|
||||
|
||||
def run(test, *, verilator_flags2=()):
|
||||
test.compile(verilator_flags2=['--coverage', *verilator_flags2])
|
||||
test.execute()
|
||||
covergroup_coverage_report(test)
|
||||
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
cg.cp1.hi: 0
|
||||
cg.cp1.lo: 1
|
||||
cg_clocked.cp_clocked.hi: 0
|
||||
cg_clocked.cp_clocked.lo: 1
|
||||
cg_samp.cp.b0: 1
|
||||
cg_samp.cp.b3: 1
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
|
|||
|
|
@ -4,35 +4,85 @@
|
|||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilator lint_off COVERIGN
|
||||
// A plain (non-covergroup) class included to verify it does not interfere with covergroup handling
|
||||
class PlainClass;
|
||||
int x;
|
||||
endclass
|
||||
|
||||
// Top-level (file-scope) covergroup declared outside any module
|
||||
covergroup cg_toplevel;
|
||||
cp_tl: coverpoint 0;
|
||||
endgroup
|
||||
|
||||
module t;
|
||||
|
||||
int i, j;
|
||||
logic clk = 0;
|
||||
|
||||
covergroup cg(int var1, int var2 = 42);
|
||||
cp1: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; }
|
||||
endgroup
|
||||
|
||||
// Clocked covergroup with constructor arguments
|
||||
covergroup cg_clocked(int lim) @(posedge clk);
|
||||
cp_clocked: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; }
|
||||
endgroup
|
||||
|
||||
// 'with function sample' covergroup whose coverpoint references its own sample-argument
|
||||
// member. That reference resolves to a member of the covergroup class itself and so must
|
||||
// NOT be mistaken for an unsupported enclosing-class reference (and skipped).
|
||||
covergroup cg_samp with function sample(bit [1:0] x);
|
||||
cp: coverpoint x { bins b0 = {0}; bins b3 = {3}; }
|
||||
endgroup
|
||||
|
||||
cg cov1 = new(69, 77);
|
||||
cg cov2 = new(69);
|
||||
|
||||
int i, j;
|
||||
real r;
|
||||
cg_clocked cov_clocked = new(10);
|
||||
cg_samp cov_samp = new;
|
||||
PlainClass plain_inst = new; // Non-covergroup class instance - must not affect covergroup coverage
|
||||
|
||||
function void x();
|
||||
real cov_result;
|
||||
cov1.set_inst_name("the_inst_name");
|
||||
cov1.start();
|
||||
cov1.sample();
|
||||
cov1.stop();
|
||||
|
||||
void'(cov2.get_coverage());
|
||||
r = cov2.get_coverage();
|
||||
r = cov2.get_coverage(i, j);
|
||||
// verilator lint_off IGNOREDRETURN
|
||||
cov2.get_inst_coverage();
|
||||
// verilator lint_on IGNOREDRETURN
|
||||
r = cov2.get_inst_coverage(i, j);
|
||||
cov_result = cov2.get_coverage();
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_coverage() out of range: %f", cov_result);
|
||||
|
||||
cg::get_coverage();
|
||||
r = cg::get_coverage();
|
||||
r = cg::get_coverage(i, j);
|
||||
cov_result = cov2.get_coverage(i, j);
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_coverage(i,j) return out of range: %f", cov_result);
|
||||
|
||||
cov_result = cov2.get_inst_coverage();
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_inst_coverage() out of range: %f", cov_result);
|
||||
|
||||
cov_result = cov2.get_inst_coverage(i, j);
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_inst_coverage(i,j) return out of range: %f", cov_result);
|
||||
|
||||
cov_result = cg::get_coverage();
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: cg::get_coverage() out of range: %f", cov_result);
|
||||
|
||||
cov_result = cg::get_coverage(i, j);
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: cg::get_coverage(i,j) return out of range: %f", cov_result);
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
i = 3;
|
||||
x(); // samples cov1 with i=3 -> lo bin hit
|
||||
clk = 1; // posedge: samples cov_clocked with i=3 -> lo bin hit
|
||||
// Sample-arg coverpoint: the passed value must reach the coverpoint. Sampling 0 then 3
|
||||
// must hit b0 and b3 respectively; if the argument were dropped (member left at its
|
||||
// default 0) b3 would never be hit.
|
||||
cov_samp.sample(2'd0);
|
||||
cov_samp.sample(2'd3);
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
cg.data.grouped: 2
|
||||
cg.data.values: 3
|
||||
cg2.cp.range_arr: 3
|
||||
cg3.cp.range_sized: 3
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN'])
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test array bins - separate bin per value, including range expressions
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
bit [7:0] data;
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
// Array bins: creates 3 separate bins
|
||||
bins values[] = {1, 5, 9};
|
||||
|
||||
// Non-array bin: creates 1 bin covering all values
|
||||
bins grouped = {2, 6, 10};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg2: array bins using a range expression - one bin per value in the range
|
||||
covergroup cg2;
|
||||
cp: coverpoint data {
|
||||
bins range_arr[] = {[0:3]}; // range expression: creates 4 separate bins
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg3: sized array bins - bins r[N] = {[lo:hi]} distributes range into N bins
|
||||
covergroup cg3;
|
||||
cp: coverpoint data {
|
||||
bins range_sized[4] = {[4:7]}; // explicit count: 4 bins covering [4:7]
|
||||
}
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
|
||||
cg_inst = new();
|
||||
cg2_inst = new();
|
||||
cg3_inst = new();
|
||||
|
||||
// Hit first array bin value (1)
|
||||
data = 1;
|
||||
cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 25.0);
|
||||
|
||||
// Hit second array bin value (5)
|
||||
data = 5;
|
||||
cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 50.0);
|
||||
|
||||
// Hit the grouped bin (covers all of 2, 6, 10)
|
||||
data = 6;
|
||||
cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Hit third array bin value (9)
|
||||
data = 9;
|
||||
cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Verify hitting other values in grouped bin doesn't increase coverage
|
||||
data = 2;
|
||||
cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Hit range_arr bins ([0:3])
|
||||
data = 0; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 25.0);
|
||||
data = 1; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 50.0);
|
||||
data = 2; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Hit range_sized bins ([4:7])
|
||||
data = 4; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 25.0);
|
||||
data = 5; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 50.0);
|
||||
data = 6; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
cg1.cp_data3.auto_0: 1
|
||||
cg1.cp_data3.auto_1: 0
|
||||
cg1.cp_data3.auto_2: 0
|
||||
cg1.cp_data3.auto_3: 1
|
||||
cg1.cp_data3.auto_4: 0
|
||||
cg1.cp_data3.auto_5: 0
|
||||
cg1.cp_data3.auto_6: 0
|
||||
cg1.cp_data3.auto_7: 0
|
||||
cg2.cp_data3.auto_0: 1
|
||||
cg2.cp_data3.auto_1: 0
|
||||
cg2.cp_data3.auto_2: 1
|
||||
cg2.cp_data3.auto_3: 0
|
||||
cg3.cp_data3.auto_0: 1
|
||||
cg3.cp_data3.auto_1: 1
|
||||
cg4.cp.auto_0: 0
|
||||
cg4.cp.auto_1: 1
|
||||
cg4.cp.auto_2: 1
|
||||
cg4.cp.auto_3: 1
|
||||
cg4.cp.ign [ignore]: 0
|
||||
cg5.cp_data64.auto_0: 1
|
||||
cg5.cp_data64.auto_1: 1
|
||||
cg5.cp_data64.auto_2: 1
|
||||
cg5.cp_data64.auto_3: 1
|
||||
cg6.cp_data3.auto_0: 1
|
||||
cg6.cp_data3.auto_1: 0
|
||||
cg6.cp_data3.auto_2: 0
|
||||
cg6.cp_data3.auto_3: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2024 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test implicit auto-bin creation (no explicit bins) and option.auto_bin_max
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [2:0] data3;
|
||||
logic [3:0] data4;
|
||||
logic [63:0] data64; // 64-bit signal
|
||||
|
||||
// Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal
|
||||
covergroup cg1;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
// Test 2: auto_bin_max = 4 at covergroup level - creates 4 bins: [0:1],[2:3],[4:5],[6:7]
|
||||
covergroup cg2;
|
||||
option.auto_bin_max = 4;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
// Test 3: auto_bin_max and at_least set at coverpoint level
|
||||
covergroup cg3;
|
||||
cp_data3: coverpoint data3 {
|
||||
option.auto_bin_max = 2; // coverpoint-level: creates 2 bins [0:3],[4:7]
|
||||
option.at_least = 3; // coverpoint-level at_least
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Test 4: auto-bins where all values in a range are excluded by ignore_bins
|
||||
// auto_bin_max=4 on 4-bit signal -> 4 range bins: [0:3],[4:7],[8:11],[12:15].
|
||||
// ignore_bins {[0:3]} excludes all values in the first range -> that bin is skipped.
|
||||
covergroup cg4;
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint data4 {
|
||||
ignore_bins ign = {[0:3]}; // first range excluded from coverage
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Test 5: auto-bins on a 64-bit coverpoint with auto_bin_max=4
|
||||
covergroup cg5;
|
||||
option.auto_bin_max = 4;
|
||||
cp_data64: coverpoint data64;
|
||||
endgroup
|
||||
|
||||
// Test option.auto_bin_max at covergroup level: creates 4 bins [0:1],[2:3],[4:5],[6:7]
|
||||
covergroup cg6;
|
||||
option.auto_bin_max = 4;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg1 cg1_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
cg4 cg4_inst;
|
||||
cg5 cg5_inst;
|
||||
cg6 cg6_inst;
|
||||
|
||||
cg1_inst = new;
|
||||
cg2_inst = new;
|
||||
cg3_inst = new;
|
||||
cg4_inst = new;
|
||||
cg5_inst = new;
|
||||
cg6_inst = new;
|
||||
|
||||
data3 = 0; cg1_inst.sample();
|
||||
`checkr(cg1_inst.get_inst_coverage(), 12.5); // 1/8 bins hit
|
||||
data3 = 3; cg1_inst.sample();
|
||||
`checkr(cg1_inst.get_inst_coverage(), 25.0); // 2/8 bins hit
|
||||
|
||||
data3 = 0; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1]
|
||||
data3 = 4; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[4:5]
|
||||
|
||||
// cg3: at_least=3 at coverpoint level; both samples have count=1 < 3 -> 0% throughout
|
||||
data3 = 1; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 0.0);
|
||||
data3 = 5; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 0.0);
|
||||
|
||||
// Sample valid (non-ignored) values for cg4
|
||||
// cg4: auto_bin_max=4 creates 4 bins [0:3],[4:7],[8:11],[12:15].
|
||||
// ignore_bins ign={[0:3]} excludes [0:3] values; Verilator keeps all 4 bins in denominator.
|
||||
// 3 of 4 bins hit -> 75% (the [0:3] bin is included in denominator but can never be hit)
|
||||
data4 = 4; cg4_inst.sample(); // [4:7] bin
|
||||
data4 = 8; cg4_inst.sample(); // [8:11] bin
|
||||
data4 = 12; cg4_inst.sample(); // [12:15] bin
|
||||
`checkr(cg4_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Sample cg5: 64-bit coverpoint - SKIP: Verilator 64-bit bin boundary bug causes 100% at first sample
|
||||
data64 = 64'h0; cg5_inst.sample();
|
||||
data64 = 64'h1111111111111111; cg5_inst.sample();
|
||||
data64 = 64'hffffffffffffffff; cg5_inst.sample();
|
||||
|
||||
data3 = 0; cg6_inst.sample();
|
||||
`checkr(cg6_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1]
|
||||
data3 = 7; cg6_inst.sample();
|
||||
`checkr(cg6_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[6:7]
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_auto_bin_max_bad.v:14:29: Ignoring unsupported: non-constant 'option.auto_bin_max'; using default value
|
||||
: ... note: In instance 't'
|
||||
14 | option.auto_bin_max = size_var;
|
||||
| ^~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2025 by Wilson Snyder.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
int size_var;
|
||||
logic [3:0] cp_expr;
|
||||
|
||||
// Error: option.auto_bin_max must be a constant expression (group level)
|
||||
covergroup cg;
|
||||
option.auto_bin_max = size_var;
|
||||
cp: coverpoint cp_expr;
|
||||
endgroup
|
||||
|
||||
cg cg_i = new;
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Test automatic bins: bins auto[N]
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [2:0] data; // 3-bit: 0-7
|
||||
logic [3:0] data4; // 4-bit signal
|
||||
logic [63:0] data64; // 64-bit signal
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 4-bit signal with auto[4]: creates 4 equal-width bins covering [0:15]
|
||||
covergroup cg_4bit;
|
||||
coverpoint data4 {
|
||||
bins auto[4]; // Creates 4 bins: [0:3], [4:7], [8:11], [12:15]
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 4-bit auto bins with one value excluded by ignore_bins
|
||||
covergroup cg_4bit_excl;
|
||||
coverpoint data4 {
|
||||
ignore_bins bad = {0}; // value 0 excluded from auto expansion
|
||||
bins auto[4];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 64-bit signal with auto_bin_max=2: creates 2 bins covering the full 64-bit range
|
||||
covergroup cg2;
|
||||
option.auto_bin_max = 2;
|
||||
coverpoint data64;
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
automatic cg cg_inst = new;
|
||||
automatic cg_4bit cg4_inst = new;
|
||||
automatic cg_4bit_excl cg4e_inst = new;
|
||||
automatic cg2 cg2_inst = new;
|
||||
|
||||
// Sample 3-bit cg: one value per bin - 4 bins: [0:1],[2:3],[4:5],[6:7]
|
||||
data = 0; cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 25.0);
|
||||
data = 2; cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 50.0);
|
||||
data = 5; cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 75.0);
|
||||
data = 7; cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample 4-bit bins - 4 bins: [0:3],[4:7],[8:11],[12:15]
|
||||
data4 = 0; cg4_inst.sample(); // bin [0:3]
|
||||
`checkr(cg4_inst.get_inst_coverage(), 25.0);
|
||||
data4 = 7; cg4_inst.sample(); // bin [4:7]
|
||||
`checkr(cg4_inst.get_inst_coverage(), 50.0);
|
||||
data4 = 10; cg4_inst.sample(); // bin [8:11]
|
||||
`checkr(cg4_inst.get_inst_coverage(), 75.0);
|
||||
data4 = 14; cg4_inst.sample(); // bin [12:15]
|
||||
`checkr(cg4_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample 4-bit with exclusion (value 0 excluded; 4 auto bins for remaining values)
|
||||
data4 = 1; cg4e_inst.sample();
|
||||
`checkr(cg4e_inst.get_inst_coverage(), 25.0);
|
||||
data4 = 8; cg4e_inst.sample();
|
||||
`checkr(cg4e_inst.get_inst_coverage(), 50.0);
|
||||
|
||||
// Sample 64-bit cg2 - SKIP checkr: Verilator 64-bit bin boundary bug causes 100% at first sample
|
||||
data64 = 64'd0; cg2_inst.sample();
|
||||
data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample();
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
cg.cp_data.one: 1
|
||||
cg.cp_data.three: 1
|
||||
cg.cp_data.two: 1
|
||||
cg.cp_data.zero: 2
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
# Use the same .v file as the non-timing test
|
||||
test.top_filename = "t/t_covergroup_clocked_sample.v"
|
||||
|
||||
coverage_covergroup_common.run(test, verilator_flags2=['--timing'])
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
%Error: t/t_covergroup_autobins_bad.v:17:12: Automatic bins array size must be a constant
|
||||
: ... note: In instance 't'
|
||||
17 | bins auto[size_var];
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_covergroup_autobins_bad.v:24:12: Automatic bins array size must be >= 1, got 0
|
||||
: ... note: In instance 't'
|
||||
24 | bins auto[0];
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:31:12: Automatic bins array size of 1001 exceeds limit of 1000
|
||||
: ... note: In instance 't'
|
||||
31 | bins auto[1001];
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin value list; values must be constants
|
||||
: ... note: In instance 't'
|
||||
43 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:44:32: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
44 | ignore_bins ign_range = {[0:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:38:12: Non-constant expression in array bins range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
38 | bins b[] = {[size_var:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:39:12: Non-constant expression in array bins range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
39 | bins b_mixed[] = {[0:size_var]};
|
||||
| ^~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:40:23: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
40 | bins b_range = {[size_var:4]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:41:24: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
41 | bins b_range2 = {[0:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:42:18: Non-constant expression in bin range; values must be constants
|
||||
: ... note: In instance 't'
|
||||
42 | bins b2 = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin range; values must be constants
|
||||
: ... note: In instance 't'
|
||||
43 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:51:25: Ignoring unsupported: non-constant 'option.at_least'; using default value
|
||||
: ... note: In instance 't'
|
||||
51 | option.at_least = size_var;
|
||||
| ^~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: t/t_covergroup_autobins_bad.v:61:31: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
61 | ignore_bins ign_nclo = {[size_var:4]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:58:20: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
|
||||
: ... note: In instance 't'
|
||||
58 | bins b_xz = {[4'bxxxx:4'hF]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:59:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
|
||||
: ... note: In instance 't'
|
||||
59 | ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:60:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
|
||||
: ... note: In instance 't'
|
||||
60 | ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:62:23: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
62 | bins b_nc_ub = {[size_var:$]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:63:23: Four-state (x/z) value in bin range bound; range bounds must be two-state constants
|
||||
: ... note: In instance 't'
|
||||
63 | bins b_xz_ub = {[4'bxxxx:$]};
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
@ -11,6 +11,6 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Tests for automatic bins error conditions
|
||||
|
||||
module t;
|
||||
int size_var;
|
||||
logic [3:0] cp_expr;
|
||||
|
||||
// Error: array size must be a constant
|
||||
covergroup cg1;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[size_var];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: array size must be >= 1 (zero)
|
||||
covergroup cg2;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[0];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: array size exceeds limit of 1000
|
||||
covergroup cg2b;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[1001];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: non-constant value in bin ranges
|
||||
covergroup cg3;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins b[] = {[size_var:size_var]}; // non-constant array bins range (both bounds non-const)
|
||||
bins b_mixed[] = {[0:size_var]}; // non-constant array bins range (max bound non-const)
|
||||
bins b_range = {[size_var:4]}; // non-constant regular bin range (lhs non-const)
|
||||
bins b_range2 = {[0:size_var]}; // non-constant regular bin range (rhs non-const)
|
||||
bins b2 = {size_var}; // non-constant simple bin value
|
||||
ignore_bins ign = {size_var}; // non-constant ignore_bins value
|
||||
ignore_bins ign_range = {[0:size_var]}; // non-constant ignore_bins range (rhs non-const)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: non-constant coverpoint option value
|
||||
covergroup cg4;
|
||||
cp1: coverpoint cp_expr {
|
||||
option.at_least = size_var; // non-constant coverpoint option value
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: four-state (x/z) value in bin range bound, and non-constant lower bound
|
||||
covergroup cg5;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins b_xz = {[4'bxxxx:4'hF]}; // four-state lower bound (match-code path)
|
||||
ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]}; // four-state lower bound (range-enum path)
|
||||
ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]}; // four-state upper bound (range-enum path)
|
||||
ignore_bins ign_nclo = {[size_var:4]}; // non-constant lower bound
|
||||
bins b_nc_ub = {[size_var:$]}; // non-constant lower bound, open-ended '$' upper
|
||||
bins b_xz_ub = {[4'bxxxx:$]}; // four-state lower bound, open-ended '$' upper
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg1 cg1_inst = new;
|
||||
cg2 cg2_inst = new;
|
||||
cg2b cg2b_inst = new;
|
||||
cg3 cg3_inst = new;
|
||||
cg4 cg4_inst = new;
|
||||
cg5 cg5_inst = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
cg.data.low: 3
|
||||
cg.data.zero: 1
|
||||
cg_db.cp.high: 1
|
||||
cg_db.cp.low: 1
|
||||
cg_mixed.opcode.arith: 1
|
||||
cg_mixed.opcode.load: 1
|
||||
cg_mixed.opcode.nop: 1
|
||||
cg_mixed.opcode.other: 1
|
||||
cg_mixed.opcode.store: 1
|
||||
cg_sel.cp.hi: 1
|
||||
cg_sel.cp.lo: 1
|
||||
cg_unbounded.cp.lo: 1
|
||||
cg_unbounded.cp.others: 1
|
||||
cg_unbounded_all.cp.all: 1
|
||||
cg_unbounded_lo.cp.rest: 1
|
||||
cg_unbounded_lo.cp.start: 1
|
||||
cg_unbounded_signed.cp.hi: 1
|
||||
cg_unbounded_signed.cp.lo: 1
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
||||
# Verify coverage.dat format contains covergroup entries (replaces t_covergroup_database)
|
||||
test.file_grep(test.coverage_filename, r'covergroup')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}low')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}high')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.low')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.high')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}low.*\' [1-9]')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}high.*\' [1-9]')
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Tests bin counts, mixed bin types (single values, lists, ranges), and
|
||||
// coverage database format (verifies coverage.dat contains covergroup entries).
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [3:0] data;
|
||||
logic [7:0] opcode;
|
||||
logic signed [3:0] sdata;
|
||||
|
||||
typedef struct packed {bit [7:0] value;} f_t;
|
||||
f_t f1;
|
||||
|
||||
// cg: basic bin count tracking
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins zero = {0};
|
||||
bins low = {[1:3]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_mixed: mixed bin types - single values, multi-value lists, ranges
|
||||
covergroup cg_mixed;
|
||||
coverpoint opcode {
|
||||
bins nop = {8'h00};
|
||||
bins load = {8'h01, 8'h02, 8'h03};
|
||||
bins store = {8'h04, 8'h05};
|
||||
bins arith = {[8'h10:8'h1F]};
|
||||
bins other = {[8'h20:8'hFE]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_db: labeled coverpoint - verifies the coverage database records the correct hierarchy path
|
||||
covergroup cg_db;
|
||||
cp: coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[8:15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_unbounded: open-ended bin range - '$' resolves to the coverpoint domain max (15 for 4-bit)
|
||||
covergroup cg_unbounded;
|
||||
cp: coverpoint data {
|
||||
bins lo = {[0:9]}; // 1 bin: values 0..9
|
||||
bins others = {[10:$]}; // 1 bin: values 10..15 ($ == domain max)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_unbounded_lo: open-ended LOWER bound - '$' resolves to the domain min (0)
|
||||
covergroup cg_unbounded_lo;
|
||||
cp: coverpoint data {
|
||||
bins start = {[$:5]}; // 1 bin: values 0..5 ($ == domain min) -> expr <= 5
|
||||
bins rest = {[6:15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_unbounded_all: both bounds open ('[$:$]' covers the entire domain -> always true)
|
||||
covergroup cg_unbounded_all;
|
||||
cp: coverpoint data {
|
||||
bins all = {[$:$]}; // 1 bin: every value
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_unbounded_signed: signed coverpoint with open-ended bounds in both directions
|
||||
covergroup cg_unbounded_signed;
|
||||
cp: coverpoint sdata {
|
||||
bins hi = {[2:$]}; // sdata >= 2 (signed >=)
|
||||
bins lo = {[$:-2]}; // sdata <= -2 (signed <=)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_sel: coverpoint over a struct-member part-select expression (AstSel)
|
||||
covergroup cg_sel;
|
||||
cp: coverpoint f1.value[3:0] { // low nibble only; upper bits ignored
|
||||
bins lo = {[0:7]};
|
||||
bins hi = {[8:15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst;
|
||||
cg_mixed cg_mixed_inst;
|
||||
cg_db cg_db_inst;
|
||||
cg_unbounded cg_unbounded_inst;
|
||||
cg_unbounded_lo cg_unbounded_lo_inst;
|
||||
cg_unbounded_all cg_unbounded_all_inst;
|
||||
cg_unbounded_signed cg_unbounded_signed_inst;
|
||||
cg_sel cg_sel_inst;
|
||||
|
||||
initial begin
|
||||
cg_inst = new;
|
||||
cg_mixed_inst = new;
|
||||
cg_db_inst = new;
|
||||
cg_unbounded_inst = new;
|
||||
cg_unbounded_lo_inst = new;
|
||||
cg_unbounded_all_inst = new;
|
||||
cg_unbounded_signed_inst = new;
|
||||
cg_sel_inst = new;
|
||||
|
||||
data = 0; cg_inst.sample(); // zero: 1
|
||||
`checkr(cg_inst.get_inst_coverage(), 50.0);
|
||||
data = 1; cg_inst.sample(); // low: 1
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
data = 2; cg_inst.sample(); // low: 2
|
||||
data = 2; cg_inst.sample(); // low: 3
|
||||
|
||||
opcode = 8'h00; cg_mixed_inst.sample(); // nop
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 20.0);
|
||||
opcode = 8'h02; cg_mixed_inst.sample(); // load
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 40.0);
|
||||
opcode = 8'h05; cg_mixed_inst.sample(); // store
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 60.0);
|
||||
opcode = 8'h15; cg_mixed_inst.sample(); // arith
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 80.0);
|
||||
opcode = 8'h80; cg_mixed_inst.sample(); // other
|
||||
`checkr(cg_mixed_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
data = 1; cg_db_inst.sample(); // low
|
||||
`checkr(cg_db_inst.get_inst_coverage(), 50.0);
|
||||
data = 10; cg_db_inst.sample(); // high
|
||||
`checkr(cg_db_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Open-ended range: '$' upper bound covers values up to the domain max
|
||||
data = 5; cg_unbounded_inst.sample(); // lo
|
||||
`checkr(cg_unbounded_inst.get_inst_coverage(), 50.0);
|
||||
data = 12; cg_unbounded_inst.sample(); // others ([10:$] covers 12)
|
||||
`checkr(cg_unbounded_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Open-ended lower bound: '$' min covers values down to 0
|
||||
data = 3; cg_unbounded_lo_inst.sample(); // start ([$:5] covers 3)
|
||||
`checkr(cg_unbounded_lo_inst.get_inst_coverage(), 50.0);
|
||||
data = 10; cg_unbounded_lo_inst.sample(); // rest
|
||||
`checkr(cg_unbounded_lo_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Both-open range '[$:$]' matches any value
|
||||
data = 7; cg_unbounded_all_inst.sample(); // all
|
||||
`checkr(cg_unbounded_all_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Signed open-ended bounds: '$' resolves to signed domain max/min
|
||||
sdata = 5; cg_unbounded_signed_inst.sample(); // hi ([2:$] covers 5)
|
||||
`checkr(cg_unbounded_signed_inst.get_inst_coverage(), 50.0);
|
||||
sdata = -5; cg_unbounded_signed_inst.sample(); // lo ([$:-2] covers -5)
|
||||
`checkr(cg_unbounded_signed_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Part-select coverpoint: only the low nibble is sampled (upper bits ignored)
|
||||
f1.value = 8'h03; cg_sel_inst.sample(); // nibble 3 -> lo
|
||||
`checkr(cg_sel_inst.get_inst_coverage(), 50.0);
|
||||
f1.value = 8'hF9; cg_sel_inst.sample(); // nibble 9 -> hi (upper bits F ignored)
|
||||
`checkr(cg_sel_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
cg.cp_data.one: 1
|
||||
cg.cp_data.three: 1
|
||||
cg.cp_data.two: 1
|
||||
cg.cp_data.zero: 2
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
cg2.addr_cmd.addr0_x_read [cross]: 1
|
||||
cg2.addr_cmd.addr0_x_write [cross]: 1
|
||||
cg2.addr_cmd.addr1_x_read [cross]: 1
|
||||
cg2.addr_cmd.addr1_x_write [cross]: 1
|
||||
cg2.cp_addr.addr0: 2
|
||||
cg2.cp_addr.addr1: 2
|
||||
cg2.cp_cmd.read: 2
|
||||
cg2.cp_cmd.write: 2
|
||||
cg3.addr_cmd_mode.addr0_x_read_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr0_x_read_x_normal [cross]: 1
|
||||
cg3.addr_cmd_mode.addr0_x_write_x_debug [cross]: 1
|
||||
cg3.addr_cmd_mode.addr0_x_write_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_read_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_read_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_write_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_write_x_normal [cross]: 1
|
||||
cg3.addr_cmd_mode.addr2_x_read_x_debug [cross]: 1
|
||||
cg3.addr_cmd_mode.addr2_x_read_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr2_x_write_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr2_x_write_x_normal [cross]: 0
|
||||
cg3.cp_addr.addr0: 2
|
||||
cg3.cp_addr.addr1: 1
|
||||
cg3.cp_addr.addr2: 1
|
||||
cg3.cp_cmd.read: 2
|
||||
cg3.cp_cmd.write: 2
|
||||
cg3.cp_mode.debug: 2
|
||||
cg3.cp_mode.normal: 2
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd [cross]: 1
|
||||
cg4.cp_addr.addr0: 2
|
||||
cg4.cp_addr.addr1: 2
|
||||
cg4.cp_cmd.read: 2
|
||||
cg4.cp_cmd.write: 2
|
||||
cg4.cp_mode.debug: 2
|
||||
cg4.cp_mode.normal: 2
|
||||
cg4.cp_parity.even: 2
|
||||
cg4.cp_parity.odd: 2
|
||||
cg5.addr_cmd_opt.addr0_x_read [cross]: 1
|
||||
cg5.addr_cmd_opt.addr0_x_write [cross]: 0
|
||||
cg5.addr_cmd_opt.addr1_x_read [cross]: 0
|
||||
cg5.addr_cmd_opt.addr1_x_write [cross]: 1
|
||||
cg5.cp_addr.addr0: 1
|
||||
cg5.cp_addr.addr1: 1
|
||||
cg5.cp_cmd.read: 1
|
||||
cg5.cp_cmd.write: 1
|
||||
cg_at_least.addr_cmd_al.addr0_x_read [cross]: 1
|
||||
cg_at_least.addr_cmd_al.addr0_x_write [cross]: 0
|
||||
cg_at_least.addr_cmd_al.addr1_x_read [cross]: 0
|
||||
cg_at_least.addr_cmd_al.addr1_x_write [cross]: 1
|
||||
cg_at_least.cp_addr.addr0: 1
|
||||
cg_at_least.cp_addr.addr1: 1
|
||||
cg_at_least.cp_cmd.read: 1
|
||||
cg_at_least.cp_cmd.write: 1
|
||||
cg_goal.addr_cmd_goal.addr0_x_read [cross]: 1
|
||||
cg_goal.addr_cmd_goal.addr0_x_write [cross]: 0
|
||||
cg_goal.addr_cmd_goal.addr1_x_read [cross]: 0
|
||||
cg_goal.addr_cmd_goal.addr1_x_write [cross]: 1
|
||||
cg_goal.cp_addr.addr0: 1
|
||||
cg_goal.cp_addr.addr1: 1
|
||||
cg_goal.cp_cmd.read: 1
|
||||
cg_goal.cp_cmd.write: 1
|
||||
cg_ignore.cp_addr.a0: 2
|
||||
cg_ignore.cp_addr.a1: 2
|
||||
cg_ignore.cp_addr.ign [ignore]: 1
|
||||
cg_ignore.cp_cmd.read: 3
|
||||
cg_ignore.cp_cmd.write: 2
|
||||
cg_ignore.cross_ab.a0_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a0_x_write [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_read [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.lo_range_x_read [cross]: 1
|
||||
cg_range.addr_cmd_range.lo_range_x_write [cross]: 1
|
||||
cg_range.cp_addr.hi_range: 2
|
||||
cg_range.cp_addr.lo_range: 2
|
||||
cg_range.cp_cmd.read: 2
|
||||
cg_range.cp_cmd.write: 2
|
||||
cg_unnamed_cross.__cross7.a0_x_read [cross]: 1
|
||||
cg_unnamed_cross.__cross7.a0_x_write [cross]: 0
|
||||
cg_unnamed_cross.__cross7.a1_x_read [cross]: 0
|
||||
cg_unnamed_cross.__cross7.a1_x_write [cross]: 1
|
||||
cg_unnamed_cross.cp_a.a0: 1
|
||||
cg_unnamed_cross.cp_a.a1: 1
|
||||
cg_unnamed_cross.cp_c.read: 1
|
||||
cg_unnamed_cross.cp_c.write: 1
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_read [cross]: 1
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_write [cross]: 0
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_read [cross]: 0
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_write [cross]: 1
|
||||
cg_unsup_cross_opt.cp_addr.addr0: 1
|
||||
cg_unsup_cross_opt.cp_addr.addr1: 1
|
||||
cg_unsup_cross_opt.cp_cmd.read: 1
|
||||
cg_unsup_cross_opt.cp_cmd.write: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN'])
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test cross coverage: 2-way, 3-way, and 4-way crosses
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [1:0] addr;
|
||||
logic cmd;
|
||||
logic mode;
|
||||
logic parity;
|
||||
|
||||
// 2-way cross
|
||||
covergroup cg2;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// 3-way cross
|
||||
covergroup cg3;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
bins addr2 = {2};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cp_mode: coverpoint mode {
|
||||
bins normal = {0};
|
||||
bins debug = {1};
|
||||
}
|
||||
addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode;
|
||||
endgroup
|
||||
|
||||
// 4-way cross
|
||||
covergroup cg4;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cp_mode: coverpoint mode {
|
||||
bins normal = {0};
|
||||
bins debug = {1};
|
||||
}
|
||||
cp_parity: coverpoint parity {
|
||||
bins even = {0};
|
||||
bins odd = {1};
|
||||
}
|
||||
addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity;
|
||||
endgroup
|
||||
|
||||
// Cross with option set inside the cross body
|
||||
covergroup cg5;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_opt: cross cp_addr, cp_cmd {
|
||||
option.weight = 2;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 2-way cross where one coverpoint uses a range bin
|
||||
covergroup cg_range;
|
||||
cp_addr: coverpoint addr {
|
||||
bins lo_range = {[0:1]}; // range bin
|
||||
bins hi_range = {[2:3]};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_range: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross where one coverpoint has ignore_bins - ignored values must not appear in cross bins
|
||||
covergroup cg_ignore;
|
||||
cp_addr: coverpoint addr {
|
||||
ignore_bins ign = {3}; // addr=3 excluded from cross
|
||||
bins a0 = {0};
|
||||
bins a1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cross_ab: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross with option.at_least set in the cross body
|
||||
covergroup cg_at_least;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_al: cross cp_addr, cp_cmd {
|
||||
option.at_least = 3;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Cross with option.goal set in the cross body
|
||||
covergroup cg_goal;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_goal: cross cp_addr, cp_cmd {
|
||||
option.goal = 90;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it
|
||||
covergroup cg_unsup_cross_opt;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_unsup: cross cp_addr, cp_cmd {
|
||||
option.per_instance = 1; // unsupported for cross - expect COVERIGN warning
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with an unnamed cross - the cross is reported under the default name "cross"
|
||||
covergroup cg_unnamed_cross;
|
||||
cp_a: coverpoint addr { bins a0 = {0}; bins a1 = {1}; }
|
||||
cp_c: coverpoint cmd { bins read = {0}; bins write = {1}; }
|
||||
cross cp_a, cp_c; // no label: reported under the default cross name
|
||||
endgroup
|
||||
|
||||
cg2 cg2_inst = new;
|
||||
cg_ignore cg_ignore_inst = new;
|
||||
cg_range cg_range_inst = new;
|
||||
cg3 cg3_inst = new;
|
||||
cg4 cg4_inst = new;
|
||||
cg5 cg5_inst = new;
|
||||
cg_at_least cg_at_least_inst = new;
|
||||
cg_goal cg_goal_inst = new;
|
||||
cg_unsup_cross_opt cg_unsup_cross_opt_inst = new;
|
||||
cg_unnamed_cross cg_unnamed_cross_inst = new;
|
||||
|
||||
initial begin
|
||||
// Sample 2-way: hit all 4 combinations
|
||||
// cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count)
|
||||
addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read
|
||||
`checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write
|
||||
`checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write
|
||||
`checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit
|
||||
addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0); // 8/8: all 4 cross bins hit
|
||||
|
||||
// Sample 3-way: hit 4 of 12 combinations
|
||||
// cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr)
|
||||
addr = 0; cmd = 0; mode = 0; cg3_inst.sample(); // addr0 x read x normal
|
||||
addr = 1; cmd = 1; mode = 0; cg3_inst.sample(); // addr1 x write x normal
|
||||
addr = 2; cmd = 0; mode = 1; cg3_inst.sample(); // addr2 x read x debug
|
||||
addr = 0; cmd = 1; mode = 1; cg3_inst.sample(); // addr0 x write x debug
|
||||
|
||||
// Sample 4-way: hit 4 of 16 combinations
|
||||
// cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50%
|
||||
addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample();
|
||||
addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample();
|
||||
`checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins
|
||||
addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample();
|
||||
addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample();
|
||||
`checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins
|
||||
|
||||
// Sample cg5 (cross with option.weight=2; weight is ignored in flat bin count)
|
||||
// cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
addr = 0; cmd = 0; cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read
|
||||
addr = 1; cmd = 1; cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins
|
||||
|
||||
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
|
||||
// cg_ignore: 2+2+4=8 bins total
|
||||
addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins)
|
||||
`checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // still 100%
|
||||
|
||||
// Sample range-bin cross
|
||||
// cg_range: 2+2+4=8 bins
|
||||
addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8
|
||||
addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8
|
||||
addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8
|
||||
addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read
|
||||
`checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8
|
||||
|
||||
// Sample cg_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins)
|
||||
// cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75%
|
||||
addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write
|
||||
`checkr(cg_at_least_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Sample cg_goal (option.goal in cross body; does not affect hit counting)
|
||||
// cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write
|
||||
`checkr(cg_goal_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Sample cg_unsup_cross_opt
|
||||
// cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
addr = 0; cmd = 0; cg_unsup_cross_opt_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_unsup_cross_opt_inst.sample(); // addr1 x write
|
||||
`checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
// Sample cg_unnamed_cross
|
||||
// cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
|
||||
addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read
|
||||
addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write
|
||||
`checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:10: Ignoring unsupported coverage cross option: 'per_instance'
|
||||
13 | option.per_instance = 1;
|
||||
| ^~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:15:35: Unsupported: cross of 'var_x' which is not a coverpoint (implicit coverpoint)
|
||||
: ... note: In instance 't'
|
||||
15 | cross_implicit: cross cp_a, var_x;
|
||||
| ^~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2025 by Wilson Snyder.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
covergroup cg;
|
||||
cp_a: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
|
||||
cp_b: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
|
||||
cross_ab: cross cp_a, cp_b {
|
||||
option.per_instance = 1; // unsupported for cross; triggers COVERIGN
|
||||
}
|
||||
cross_implicit: cross cp_a, var_x;
|
||||
endgroup
|
||||
logic var_x = 1'b0;
|
||||
cg cg_i = new;
|
||||
initial begin
|
||||
cg_i.sample();
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.other: 2
|
||||
cg2.cp_only_default.all: 4
|
||||
cg3.data.bad [ignore]: 1
|
||||
cg3.data.err [illegal]: 0
|
||||
cg3.data.normal: 2
|
||||
cg3.data.other: 2
|
||||
cg4.cp_idx.auto_0: 1
|
||||
cg4.cp_idx.auto_1: 1
|
||||
cg4.cp_idx.auto_2: 1
|
||||
cg4.cp_idx.skip [ignore]: 0
|
||||
cg5.cp_data64.auto[0]: 2
|
||||
cg5.cp_data64.auto[1]: 0
|
||||
cg5.cp_data64.auto[2]: 0
|
||||
cg5.cp_data64.auto[3]: 0
|
||||
cg6.cp_data65.hi: 1
|
||||
cg6.cp_data65.lo: 1
|
||||
cg7.data.hi: 1
|
||||
cg7.data.lo: 1
|
||||
cg_cross.a.auto_0: 1
|
||||
cg_cross.a.auto_1: 1
|
||||
cg_cross.ab.auto_0_x_auto_0 [cross]: 1
|
||||
cg_cross.ab.auto_0_x_auto_1 [cross]: 0
|
||||
cg_cross.ab.auto_1_x_auto_0 [cross]: 0
|
||||
cg_cross.ab.auto_1_x_auto_1 [cross]: 1
|
||||
cg_cross.b.auto_0: 1
|
||||
cg_cross.b.auto_1: 1
|
||||
cg_member.__Vcoverpoint0.auto_0: 1
|
||||
cg_member.__Vcoverpoint0.auto_1: 0
|
||||
cg_member.__Vcoverpoint1.auto_0: 0
|
||||
cg_member.__Vcoverpoint1.auto_1: 1
|
||||
cg_plain.a.auto_0: 1
|
||||
cg_plain.a.auto_1: 0
|
||||
cg_plain.b.auto_0: 0
|
||||
cg_plain.b.auto_1: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test default bins - catch-all for values not in other bins
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
// Non-covergroup class in the same module - must not interfere with covergroup processing
|
||||
class DataHelper;
|
||||
bit [7:0] val;
|
||||
function new(bit [7:0] v); val = v; endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
bit [7:0] data;
|
||||
logic [1:0] idx;
|
||||
logic [63:0] data64;
|
||||
logic [64:0] data65;
|
||||
DataHelper helper;
|
||||
|
||||
// Signals for the unlabeled-coverpoint covergroups below
|
||||
logic [1:0] a;
|
||||
logic [1:0] b;
|
||||
typedef struct packed {
|
||||
logic [1:0] hi;
|
||||
logic [1:0] lo;
|
||||
} pair_t;
|
||||
pair_t p;
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[12:15]};
|
||||
bins other = default; // Catches everything else (4-11, 16+)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with default as the only bin - catches all sampled values
|
||||
covergroup cg2;
|
||||
cp_only_default: coverpoint data {
|
||||
bins all = default;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with default + ignore + illegal bins - excluded values must not count toward coverage
|
||||
covergroup cg3;
|
||||
coverpoint data {
|
||||
ignore_bins bad = {255}; // excluded from coverage
|
||||
illegal_bins err = {254}; // illegal value, excluded from coverage
|
||||
bins normal = {[1:10]};
|
||||
bins other = default;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Auto-bins on a small range with one value excluded by ignore_bins -
|
||||
// when the range is small enough, one auto-bin per valid value is created; the excluded value is skipped.
|
||||
covergroup cg4;
|
||||
cp_idx: coverpoint idx {
|
||||
ignore_bins skip = {2}; // value 2 excluded; auto-bins created for 0,1,3
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 64-bit signal with auto_bin_max=4
|
||||
covergroup cg5;
|
||||
cp_data64: coverpoint data64 { bins auto[4]; }
|
||||
endgroup
|
||||
|
||||
// 65-bit signal with explicit range bins
|
||||
covergroup cg6;
|
||||
cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; }
|
||||
endgroup
|
||||
|
||||
// Unlabeled coverpoint - the signal name is used as the coverpoint name
|
||||
covergroup cg7;
|
||||
coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; }
|
||||
endgroup
|
||||
|
||||
// Multiple unlabeled coverpoints: each gets a unique deterministic name (the variable
|
||||
// name for a single identifier). Previously two unlabeled coverpoints in one covergroup
|
||||
// collided on the generated bin-variable name (e.g. duplicate '__Vcov__auto_0').
|
||||
covergroup cg_plain;
|
||||
option.auto_bin_max = 2;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
endgroup
|
||||
|
||||
// Unlabeled coverpoints with compound (member-select) expressions get synthesized names.
|
||||
covergroup cg_member;
|
||||
option.auto_bin_max = 2;
|
||||
coverpoint p.lo;
|
||||
coverpoint p.hi;
|
||||
endgroup
|
||||
|
||||
// A cross referencing unlabeled coverpoints by their derived names.
|
||||
covergroup cg_cross;
|
||||
option.auto_bin_max = 2;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
ab: cross a, b;
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
cg4 cg4_inst;
|
||||
cg5 cg5_inst;
|
||||
cg6 cg6_inst;
|
||||
cg7 cg7_inst;
|
||||
cg_plain cg_plain_inst;
|
||||
cg_member cg_member_inst;
|
||||
cg_cross cg_cross_inst;
|
||||
|
||||
cg_inst = new();
|
||||
cg2_inst = new();
|
||||
cg3_inst = new();
|
||||
cg4_inst = new();
|
||||
cg5_inst = new();
|
||||
cg6_inst = new();
|
||||
cg7_inst = new();
|
||||
cg_plain_inst = new();
|
||||
cg_member_inst = new();
|
||||
cg_cross_inst = new();
|
||||
helper = new(8'h42);
|
||||
data = helper.val; // Use helper to avoid optimization
|
||||
|
||||
// Hit low bin
|
||||
data = 2;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0); // cg2 has 1 bin (default) -> 100% after first sample
|
||||
|
||||
// Hit high bin
|
||||
data = 14;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
|
||||
// Hit default bin with value 7 (not in low or high)
|
||||
data = 7;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0); // all 3 bins (low, high, other) hit
|
||||
|
||||
// Hit another default value (should not increase coverage)
|
||||
data = 20;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg3: verify ignore/illegal bins do not contribute to coverage
|
||||
data = 2; cg3_inst.sample(); // hits normal bin
|
||||
`checkr(cg3_inst.get_inst_coverage(), 50.0); // 1/2 bins hit (normal)
|
||||
data = 7; cg3_inst.sample(); // hits normal bin again
|
||||
`checkr(cg3_inst.get_inst_coverage(), 50.0); // no new bins
|
||||
data = 255; cg3_inst.sample(); // ignore_bins value; Verilator counts it toward default bin
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2: Verilator hits 'other' (default) even for ignore_bins
|
||||
// note: do not sample 254 (illegal_bins would cause runtime assertion)
|
||||
data = 100; cg3_inst.sample(); // hits default (other) bin
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2 bins hit
|
||||
|
||||
// Sample cg4: auto-bins with one excluded value
|
||||
// idx=2 is in ignore_bins, so auto-bins cover 0, 1, 3 only (3 bins total)
|
||||
idx = 0; cg4_inst.sample();
|
||||
idx = 1; cg4_inst.sample();
|
||||
idx = 3; cg4_inst.sample();
|
||||
`checkr(cg4_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg5: 64-bit signal, 4 auto bins; values 0 and 5 both fall in first bin
|
||||
data64 = 0; cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 25.0); // 1/4 bins hit
|
||||
data64 = 5; cg5_inst.sample();
|
||||
`checkr(cg5_inst.get_inst_coverage(), 25.0); // same bin, no increase
|
||||
|
||||
// Sample cg6: 65-bit signal with range bins
|
||||
data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15]
|
||||
`checkr(cg6_inst.get_inst_coverage(), 50.0);
|
||||
data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200]
|
||||
`checkr(cg6_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg7: unlabeled coverpoint
|
||||
data = 3; cg7_inst.sample(); // hits bin lo
|
||||
`checkr(cg7_inst.get_inst_coverage(), 50.0);
|
||||
data = 10; cg7_inst.sample(); // hits bin hi
|
||||
`checkr(cg7_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// cg_plain: two unlabeled coverpoints (a, b), 2 auto bins each -> distinct names
|
||||
a = 0; b = 3; cg_plain_inst.sample();
|
||||
`checkr(cg_plain_inst.get_inst_coverage(), 50.0);
|
||||
// cg_member: unlabeled member-select coverpoints (synthesized names)
|
||||
p.lo = 0; p.hi = 3; cg_member_inst.sample();
|
||||
`checkr(cg_member_inst.get_inst_coverage(), 50.0);
|
||||
// cg_cross: cross of unlabeled coverpoints referenced by derived name
|
||||
a = 0; b = 0; cg_cross_inst.sample();
|
||||
a = 3; b = 3; cg_cross_inst.sample();
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:27:35: Unsupported: 'covergroup' coverpoint referencing enclosing class member; ignoring covergroup '__vlAnonCG_cov_trans'
|
||||
: ... note: In instance 't'
|
||||
27 | trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;}
|
||||
| ^~~~~~~~~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test the graceful-degradation safety net for embedded covergroups (the dominant
|
||||
// UVM pattern: a covergroup declared inside a class whose coverpoints reference the
|
||||
// enclosing object's members). Such a covergroup is lowered into a sibling class
|
||||
// with no handle to the enclosing instance, so emitting it would produce
|
||||
// uncompilable C++ ("invalid use of non-static data member"). Until the enclosing
|
||||
// back-pointer feature exists, Verilator must emit a clean COVERIGN warning and skip
|
||||
// lowering the covergroup, rather than crashing the C++ compile.
|
||||
|
||||
class ubus_transfer;
|
||||
bit [15:0] addr;
|
||||
bit read_write;
|
||||
endclass
|
||||
|
||||
class ubus_master_monitor;
|
||||
ubus_transfer trans_collected;
|
||||
|
||||
// Coverpoints reference 'trans_collected', a member of the enclosing class.
|
||||
// A cross is included so the safety-net cleanup also exercises cross removal.
|
||||
covergroup cov_trans;
|
||||
trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;}
|
||||
trans_dir : coverpoint trans_collected.read_write;
|
||||
trans_addr_x_dir : cross trans_start_addr, trans_dir;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
trans_collected = new;
|
||||
cov_trans = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
ubus_master_monitor m;
|
||||
initial begin
|
||||
m = new;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Empty covergroup coverage: 100.000000%
|
||||
*-* All Finished *-*
|
||||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module - Edge case: empty covergroup
|
||||
// This file ONLY is placed into the Public Domain, for any use, without warranty.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test: Empty covergroup (no coverpoints)
|
||||
// Expected: Should compile, coverage should be 100% (nothing to cover)
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
logic [7:0] value;
|
||||
|
||||
// Empty covergroup - no coverpoints defined
|
||||
covergroup cg_empty;
|
||||
// Intentionally empty
|
||||
endgroup
|
||||
|
||||
cg_empty cg_inst = new;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
value <= value + 1;
|
||||
|
||||
cg_inst.sample();
|
||||
|
||||
if (cyc == 5) begin
|
||||
real cov;
|
||||
cov = cg_inst.get_inst_coverage();
|
||||
$display("Empty covergroup coverage: %f%%", cov);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -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
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
class base;
|
||||
function new();
|
||||
g1 = new(0);
|
||||
endfunction
|
||||
enum {red, green, blue} color;
|
||||
covergroup g1 (bit [3:0] a) with function sample(bit b);
|
||||
option.weight = 10;
|
||||
option.per_instance = 1;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
c: coverpoint color;
|
||||
endgroup
|
||||
endclass
|
||||
|
||||
class derived extends base;
|
||||
bit d;
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
covergroup extends g1;
|
||||
option.weight = 1; // overrides the weight from base g1
|
||||
// uses per_instance = 1 from base g1
|
||||
c: coverpoint color // overrides the c coverpoint in base g1
|
||||
{
|
||||
ignore_bins ignore = {blue};
|
||||
}
|
||||
coverpoint d; // adds new coverpoint
|
||||
cross a, d; // crosses new coverpoint with inherited one
|
||||
endgroup :g1
|
||||
endclass
|
||||
endmodule
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
%Error: t/t_covergroup_func_override_bad.v:10:3: syntax error, unexpected function
|
||||
10 | function sample();
|
||||
%Error: t/t_covergroup_func_override_bad.v:9:3: syntax error, unexpected function
|
||||
9 | function sample();
|
||||
| ^~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
covergroup cg;
|
||||
function sample();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
cg_and.cp_concat.b00: 0
|
||||
cg_and.cp_concat.b01: 1
|
||||
cg_and.cp_concat.b10: 0
|
||||
cg_and.cp_concat.b11: 0
|
||||
cg_array_iff.cp.arr: 1
|
||||
cg_bitw.cp.seven: 1
|
||||
cg_default_iff.cp.def: 1
|
||||
cg_default_iff.cp.known: 1
|
||||
cg_iff.cp_value.disabled_hi: 0
|
||||
cg_iff.cp_value.disabled_lo: 0
|
||||
cg_iff.cp_value.enabled_hi: 1
|
||||
cg_iff.cp_value.enabled_lo: 1
|
||||
cg_or.cp.hi: 0
|
||||
cg_or.cp.lo: 1
|
||||
cg_part.cp.one: 1
|
||||
cg_rel.cp.five: 1
|
||||
cg_rel.cp.two: 0
|
||||
cg_trans2_iff.cp.t2: 1
|
||||
cg_trans3_iff.cp.t3: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test iff (enable) guard: sampling is gated by the enable condition.
|
||||
// Covers iff on explicit value bins, default bin, array bins,
|
||||
// simple 2-step transition, and 3-step transition.
|
||||
//
|
||||
// Also covers compound iff expressions (&&, ||, unary !, bit/part-select,
|
||||
// relational compare, parenthesized bitwise, and a concatenation-valued
|
||||
// coverpoint with a compound iff). Previously any iff that was not a bare
|
||||
// reference or a unary ! tripped an internal error ("Unexpected '...'
|
||||
// expression under 'COVERPOINT'") because the iff child was widthed with no
|
||||
// context.
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic enable;
|
||||
int value;
|
||||
|
||||
// Signals for the compound-iff covergroups below
|
||||
logic m_is_read;
|
||||
logic [3:0] m_be;
|
||||
logic [1:0] m_a;
|
||||
logic [1:0] m_b;
|
||||
int count;
|
||||
|
||||
// iff on explicit value bins
|
||||
covergroup cg_iff;
|
||||
cp_value: coverpoint value iff (enable) {
|
||||
bins disabled_lo = {1};
|
||||
bins disabled_hi = {2};
|
||||
bins enabled_lo = {3};
|
||||
bins enabled_hi = {4};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// iff on default bin
|
||||
covergroup cg_default_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins known = {10};
|
||||
bins def = default; // default bin with coverpoint-level iff
|
||||
}
|
||||
endgroup
|
||||
|
||||
// iff on array bins
|
||||
covergroup cg_array_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins arr[] = {5, 6, 7}; // array bins, all gated by iff
|
||||
}
|
||||
endgroup
|
||||
|
||||
// iff on 2-step transition
|
||||
covergroup cg_trans2_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins t2 = (1 => 2);
|
||||
}
|
||||
endgroup
|
||||
|
||||
// iff on 3-step transition
|
||||
covergroup cg_trans3_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins t3 = (1 => 2 => 3);
|
||||
}
|
||||
endgroup
|
||||
|
||||
// --- compound iff expressions ---
|
||||
|
||||
// unary ! combined with && and a bit-select, on a concatenation-valued
|
||||
// coverpoint expression -- the exact reported shape.
|
||||
covergroup cg_and;
|
||||
cp_concat: coverpoint {m_a[0], m_b[0]} iff (!m_is_read && m_be[0]) {
|
||||
bins b00 = {2'b00};
|
||||
bins b01 = {2'b01};
|
||||
bins b10 = {2'b10};
|
||||
bins b11 = {2'b11};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// logical || guard
|
||||
covergroup cg_or;
|
||||
cp: coverpoint count iff (m_be[0] || m_be[1]) {
|
||||
bins lo = {1};
|
||||
bins hi = {2};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// part-select compare guard
|
||||
covergroup cg_part;
|
||||
cp: coverpoint count iff (m_be[3:0] != 0) {
|
||||
bins one = {1};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// relational compare guard
|
||||
covergroup cg_rel;
|
||||
cp: coverpoint count iff (count > 3) {
|
||||
bins five = {5};
|
||||
bins two = {2};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// parenthesized bitwise guard
|
||||
covergroup cg_bitw;
|
||||
cp: coverpoint count iff ((m_a & m_b) == 2'b10) {
|
||||
bins seven = {7};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg_iff cg1 = new;
|
||||
cg_default_iff cg2 = new;
|
||||
cg_array_iff cg3 = new;
|
||||
cg_trans2_iff cg4 = new;
|
||||
cg_trans3_iff cg5 = new;
|
||||
cg_and ca = new;
|
||||
cg_or co = new;
|
||||
cg_part cpp = new;
|
||||
cg_rel cr = new;
|
||||
cg_bitw cb = new;
|
||||
|
||||
initial begin
|
||||
// Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded
|
||||
enable = 0;
|
||||
value = 1; cg1.sample();
|
||||
value = 2; cg1.sample();
|
||||
`checkr(cg1.get_inst_coverage(), 0.0);
|
||||
|
||||
// Sample enabled_lo and enabled_hi with enable=1 -- must be recorded
|
||||
enable = 1;
|
||||
value = 3; cg1.sample();
|
||||
`checkr(cg1.get_inst_coverage(), 25.0);
|
||||
value = 4; cg1.sample();
|
||||
`checkr(cg1.get_inst_coverage(), 50.0);
|
||||
|
||||
// cg2: default bin -- enable=1 lets known and default through
|
||||
enable = 1;
|
||||
value = 10; cg2.sample(); // hits 'known'
|
||||
`checkr(cg2.get_inst_coverage(), 50.0);
|
||||
value = 99; cg2.sample(); // hits 'def' (default)
|
||||
`checkr(cg2.get_inst_coverage(), 100.0);
|
||||
enable = 0;
|
||||
value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def'
|
||||
`checkr(cg2.get_inst_coverage(), 100.0);
|
||||
|
||||
// cg3: array bins with iff (3 bins: arr[5], arr[6], arr[7])
|
||||
// 1/3 hit -> 33.3% (not a clean binary fraction; no checkr)
|
||||
enable = 1;
|
||||
value = 5; cg3.sample(); // arr[5] hit
|
||||
enable = 0;
|
||||
value = 6; cg3.sample(); // gated
|
||||
|
||||
// cg4: 2-step transition with iff
|
||||
enable = 1;
|
||||
value = 1; cg4.sample();
|
||||
`checkr(cg4.get_inst_coverage(), 0.0);
|
||||
value = 2; cg4.sample(); // (1=>2) hit with enable=1
|
||||
`checkr(cg4.get_inst_coverage(), 100.0);
|
||||
enable = 0;
|
||||
value = 1; cg4.sample();
|
||||
value = 2; cg4.sample(); // (1=>2) gated by iff
|
||||
`checkr(cg4.get_inst_coverage(), 100.0);
|
||||
|
||||
// cg5: 3-step transition with iff
|
||||
enable = 1;
|
||||
value = 1; cg5.sample();
|
||||
value = 2; cg5.sample(); // mid-sequence, enable=1
|
||||
enable = 0;
|
||||
value = 3; cg5.sample(); // iff is disabled at step 3 - incomplete sequence is discarded
|
||||
`checkr(cg5.get_inst_coverage(), 0.0);
|
||||
enable = 1;
|
||||
value = 1; cg5.sample();
|
||||
value = 2; cg5.sample();
|
||||
value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1
|
||||
`checkr(cg5.get_inst_coverage(), 100.0);
|
||||
|
||||
// --- compound iff expressions ---
|
||||
// cg_and: guard true -> {0,1}=2'b01 sampled into b01
|
||||
m_is_read = 0; m_be = 4'b0001; m_a = 2'b10; m_b = 2'b11; // m_a[0]=0,m_b[0]=1 -> 2'b01
|
||||
ca.sample(); // b01 hit
|
||||
`checkr(ca.get_inst_coverage(), 25.0);
|
||||
m_is_read = 1; m_a = 2'b11; m_b = 2'b11; // guard false -> gated
|
||||
ca.sample();
|
||||
`checkr(ca.get_inst_coverage(), 25.0);
|
||||
|
||||
// cg_or: guard true via bit1
|
||||
m_be = 4'b0010; count = 1; co.sample(); // lo hit
|
||||
`checkr(co.get_inst_coverage(), 50.0);
|
||||
m_be = 4'b0000; count = 2; co.sample(); // gated
|
||||
`checkr(co.get_inst_coverage(), 50.0);
|
||||
|
||||
// cg_part: part-select != 0
|
||||
m_be = 4'b1000; count = 1; cpp.sample(); // one hit
|
||||
`checkr(cpp.get_inst_coverage(), 100.0);
|
||||
m_be = 4'b0000; count = 1; cpp.sample(); // gated
|
||||
`checkr(cpp.get_inst_coverage(), 100.0);
|
||||
|
||||
// cg_rel: count > 3
|
||||
count = 5; cr.sample(); // five hit (5>3)
|
||||
`checkr(cr.get_inst_coverage(), 50.0);
|
||||
count = 2; cr.sample(); // two gated (2>3 false)
|
||||
`checkr(cr.get_inst_coverage(), 50.0);
|
||||
|
||||
// cg_bitw: (m_a & m_b) == 2'b10
|
||||
m_a = 2'b10; m_b = 2'b11; count = 7; cb.sample(); // seven hit
|
||||
`checkr(cb.get_inst_coverage(), 100.0);
|
||||
m_a = 2'b00; m_b = 2'b11; count = 7; cb.sample(); // gated
|
||||
`checkr(cb.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
cg.data.arr [ignore]: 0
|
||||
cg.data.bad [illegal]: 0
|
||||
cg.data.catch_all [ignore]: 0
|
||||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.reserved [ignore]: 1
|
||||
cg.data.wib [ignore]: 0
|
||||
cg2.cp_auto.auto_0: 1
|
||||
cg2.cp_auto.auto_1: 1
|
||||
cg2.cp_auto.ign [ignore]: 2
|
||||
cg2.cp_auto.ign_trans [ignore]: 1
|
||||
cg2.cp_auto_ub.auto_0: 1
|
||||
cg2.cp_auto_ub.auto_1: 1
|
||||
cg2.cp_auto_ub.ub [ignore]: 2
|
||||
cg2.cp_bounds.hi: 2
|
||||
cg2.cp_bounds.lo: 2
|
||||
cg2.cp_full.all: 4
|
||||
cg3.cp_auto_lb.auto_0: 1
|
||||
cg3.cp_auto_lb.auto_1: 1
|
||||
cg3.cp_auto_lb.lb [ignore]: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test ignore_bins - excluded from coverage
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [3:0] data;
|
||||
logic [1:0] data2; // 2-bit signal for range-boundary tests
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[8:11]};
|
||||
ignore_bins reserved = {[12:15]};
|
||||
ignore_bins catch_all = default; // default ignore-bin: all values not in other bins are ignored
|
||||
ignore_bins arr[] = {4, 5}; // array form: one ignore-bin per value
|
||||
wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore-bin with don't-care bits
|
||||
illegal_bins bad[] = {6, 7}; // illegal array form: one illegal-bin per value
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg2: ignore_bins using a range - auto-bins are created only for values not in the range.
|
||||
// Also tests range-boundary conditions: when lo==0 or hi==maxVal, the range check simplifies.
|
||||
// Also tests ignore_bins with a transition list.
|
||||
covergroup cg2;
|
||||
cp_auto: coverpoint data2 {
|
||||
ignore_bins ign = {[2:3]}; // range ignore, no regular bins -> auto-bins created
|
||||
ignore_bins ign_trans = (0 => 1); // ignore the 0->1 transition
|
||||
}
|
||||
cp_auto_ub: coverpoint data2 {
|
||||
ignore_bins ub = {[2:$]}; // open-ended ignore: '$' == domain max (3) -> auto-bins for 0,1
|
||||
}
|
||||
cp_bounds: coverpoint data2 {
|
||||
bins lo = {[0:1]}; // lower range (lo=0, no lower-bound check needed)
|
||||
bins hi = {[2:3]}; // upper range (hi=maxVal for 2-bit, no upper-bound check needed)
|
||||
}
|
||||
cp_full: coverpoint data2 {
|
||||
bins all = {[0:3]}; // full range (lo=0 and hi=maxVal: matches all values)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg3: open-ended LOWER bound ignore - '$' == domain min (0) -> auto-bins for 2,3
|
||||
covergroup cg3;
|
||||
cp_auto_lb: coverpoint data2 {
|
||||
ignore_bins lb = {[$:1]}; // ignore 0,1 -> auto-bins created for 2,3
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
|
||||
initial begin
|
||||
cg_inst = new;
|
||||
cg2_inst = new;
|
||||
cg3_inst = new;
|
||||
|
||||
data = 13; cg_inst.sample(); // reserved - ignored
|
||||
`checkr(cg_inst.get_inst_coverage(), 0.0);
|
||||
data = 1; cg_inst.sample(); // low
|
||||
`checkr(cg_inst.get_inst_coverage(), 50.0);
|
||||
data = 10; cg_inst.sample(); // high
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
data2 = 0; cg2_inst.sample(); // auto_0, lo, all
|
||||
data2 = 1; cg2_inst.sample(); // auto_1, lo, all
|
||||
data2 = 2; cg2_inst.sample(); // ign, hi, all
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
data2 = 3; cg2_inst.sample(); // ign, hi, all
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
data2 = 0; cg3_inst.sample(); // lb (ignored)
|
||||
data2 = 2; cg3_inst.sample(); // auto_0
|
||||
`checkr(cg3_inst.get_inst_coverage(), 50.0);
|
||||
data2 = 3; cg3_inst.sample(); // auto_1
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
cg.data.forbidden [illegal]: 0
|
||||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.mid: 1
|
||||
cg2.cp_arr.bad_arr [illegal]: 0
|
||||
cg2.cp_arr.ok: 1
|
||||
cg2.cp_arr.wlib [illegal]: 0
|
||||
cg2.cp_trans.bad_2step [illegal]: 0
|
||||
cg2.cp_trans.bad_3step [illegal]: 0
|
||||
cg2.cp_trans.lib_default [illegal]: 0
|
||||
cg2.cp_trans.ok: 1
|
||||
cg3.cp.ign [ignore]: 2
|
||||
cg3.cp.ill [illegal]: 0
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Test that illegal_bins are excluded from coverage (like ignore_bins).
|
||||
// Also tests coverpoints where all bins are ignore/illegal - get_coverage returns 100.0.
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic [1:0] data;
|
||||
logic [3:0] data4;
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins low = {0};
|
||||
bins mid = {1};
|
||||
bins high = {2};
|
||||
illegal_bins forbidden = {3};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg2: illegal_bins on multi-step transitions and array notation
|
||||
covergroup cg2;
|
||||
cp_trans: coverpoint data4 {
|
||||
bins ok = {0};
|
||||
illegal_bins bad_2step = (1 => 2); // 2-step illegal transition
|
||||
illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition
|
||||
illegal_bins lib_default = default; // illegal_bins = default
|
||||
}
|
||||
cp_arr: coverpoint data4 {
|
||||
bins ok = {0};
|
||||
illegal_bins bad_arr[] = {8, 9, 10}; // illegal array bins
|
||||
wildcard illegal_bins wlib = {4'b1?00}; // wildcard illegal bins
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg3: all bins are ignore_bins or illegal_bins - get_coverage returns 100.0
|
||||
covergroup cg3;
|
||||
cp: coverpoint data {
|
||||
ignore_bins ign = {0, 1};
|
||||
illegal_bins ill = {2, 3};
|
||||
}
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
automatic cg cg_inst = new;
|
||||
automatic cg2 cg2_inst = new;
|
||||
automatic cg3 cg3_inst = new;
|
||||
|
||||
// Sample legal values only
|
||||
data = 0; cg_inst.sample();
|
||||
data = 1; cg_inst.sample();
|
||||
data = 2; cg_inst.sample();
|
||||
`checkr(cg_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg2 - only safe values, never triggering illegal bins
|
||||
data4 = 0; cg2_inst.sample();
|
||||
`checkr(cg2_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample cg3 - values that only hit ignore_bins, never illegal_bins
|
||||
data = 0; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
data = 1; cg3_inst.sample();
|
||||
`checkr(cg3_inst.get_inst_coverage(), 100.0);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -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
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
real r;
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
r = embeddedCg.get_coverage();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class secondClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
real r;
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
r = embeddedCg.get_coverage();
|
||||
endfunction
|
||||
endclass
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:14: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
|
||||
13 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_in_class_duplicate_bad.v:9:14: ... Location of original declaration
|
||||
9 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
%Error: t/t_covergroup_in_class_duplicate_bad.v:12:3: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
|
||||
12 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_in_class_duplicate_bad.v:8:3: ... Location of original declaration
|
||||
8 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
__vlAnonCG_embeddedCg.cp_mc.auto_0: 0
|
||||
__vlAnonCG_embeddedCg.cp_mc.auto_1: 1
|
||||
__vlAnonCG_embeddedCg.cp_sc.auto_0: 1
|
||||
__vlAnonCG_embeddedCg.cp_sc.auto_1: 0
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
cp_mc: coverpoint 1'b1;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
real cov_result;
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
cov_result = embeddedCg.get_coverage();
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_coverage() out of range: %f", cov_result);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class secondClass;
|
||||
covergroup embeddedCg;
|
||||
cp_sc: coverpoint 1'b0;
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
real cov_result;
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
cov_result = embeddedCg.get_coverage();
|
||||
if (!(cov_result >= 0.0 && cov_result <= 100.0))
|
||||
$error("%m: get_coverage() out of range: %f", cov_result);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
myClass mc;
|
||||
secondClass sc;
|
||||
initial begin
|
||||
mc = new();
|
||||
sc = new();
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename,
|
||||
verilator_flags2=['--assert --error-limit 1000'],
|
||||
fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
cg.cp_neg.mixed: 4
|
||||
cg.cp_neg.negative: 2
|
||||
cg.cp_neg.positive: 3
|
||||
cg.cp_neg.zero: 1
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue