This commit is contained in:
Matthew Ballance 2026-03-03 20:54:13 +01:00 committed by GitHub
commit fa08863253
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
122 changed files with 6949 additions and 458 deletions

View File

@ -121,6 +121,23 @@ set(PACKAGE_VERSION ${PROJECT_VERSION})
set(CXX ${CMAKE_CXX_COMPILER})
set(AR ${CMAKE_AR})
# Detect precompiled header include flag (matches configure.ac logic)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} --help
OUTPUT_VARIABLE _cxx_help
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(_cxx_help MATCHES "include-pch")
# clang
set(CFG_CXXFLAGS_PCH_I "-include-pch")
set(CFG_GCH_IF_CLANG ".gch")
else()
# GCC
set(CFG_CXXFLAGS_PCH_I "-include")
set(CFG_GCH_IF_CLANG "")
endif()
configure_file(include/verilated_config.h.in include/verilated_config.h @ONLY)
configure_file(include/verilated.mk.in include/verilated.mk @ONLY)

View File

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

View File

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

View File

@ -184,7 +184,8 @@ Verilator supports adding code to the Verilated model to support
SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables
all forms of coverage:
- :ref:`User Coverage`
- :ref:`Property Coverage`
- :ref:`Covergroup Coverage`
- :ref:`Line Coverage`
- :ref:`Toggle Coverage`
@ -192,22 +193,113 @@ When a model with coverage is executed, it will create a coverage file for
collection and later analysis, see :ref:`Coverage Collection`.
.. _user coverage:
.. _property coverage:
Functional Coverage
-------------------
Property Coverage
-----------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate functional coverage points the user has inserted manually in
SystemVerilog code through into the Verilated model.
translate property coverage points the user has inserted manually in
SystemVerilog code into the Verilated model.
For example, the following SystemVerilog statement will add a coverage
point under the coverage name "DefaultClock":
For simple coverage points, use the ``cover property`` construct:
.. code-block:: sv
DefaultClock: cover property (@(posedge clk) cyc==3);
This adds a coverage point that tracks whether the condition has been observed.
.. _covergroup coverage:
Covergroup Coverage
-------------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate covergroup coverage points the user has inserted manually in
SystemVerilog code into the Verilated model. Verilator supports
coverpoints with value and transition bins, and cross points.
.. code-block:: sv
module top;
logic [7:0] addr;
logic cmd;
// Define a covergroup
covergroup cg;
cp_addr: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
endgroup
// Instantiate the covergroup
cg cg_inst = new;
always @(posedge clk) begin
// Sample coverage explicitly
cg_inst.sample();
end
endmodule
Supported Features
^^^^^^^^^^^^^^^^^^
* Coverpoints on integral expressions with value, range, wildcard, and transition bins
* Conditional coverpoint sampling (iff)
* Explicit and clocked sampling, with sample-function parameters
* at_least and auto_bin_max options on covergroups and coverpoints
* Cross points with auto-bins
Unsupported Features
^^^^^^^^^^^^^^^^^^^^
* Coverpoints on real (floating-point) expressions
* Coverpoint bin filtering (with)
* Coverpoint bin conditional sampling (iff)
* Transition bins with repetition operators ([\*N], [->N], [=N])
* Explicitly-typed coverpoints
* Block-event sampling
* Covergroup inheritance (extends)
* Cross points with user-defined bins
Functional Coverage Data Format
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Functional coverage data is stored in the coverage data file (typically
:file:`coverage.dat`) using the standard Verilator coverage format. Each
functional coverage bin is recorded as a coverage point with:
* **Type**: ``funccov`` - identifies the record as functional coverage
* **Page**: ``v_funccov/<covergroup_name>`` - groups bins by their covergroup
* **Hierarchy**: ``<covergroup>.<coverpoint>.<bin>`` for coverpoints, or
``<covergroup>.<cross>.<bin>`` for cross coverage
* **Count**: Number of times the bin was hit during simulation
Example coverage.dat entries:
.. code-block::
C 'tfunccovpagev_funccov/cgftest.vl28hcg.cp_a.low' 150
C 'tfunccovpagev_funccov/cgftest.vl29hcg.cp_a.high' 75
C 'tfunccovpagev_funccov/cgftest.vl35hcg.cross_ab.a0_b1' 25
To filter functional coverage data, use the :option:`--filter-type` option
with :command:`verilator_coverage`:
.. code-block:: bash
# Only process functional coverage
$ verilator_coverage --filter-type funccov --annotate report coverage.dat
# Exclude functional coverage
$ verilator_coverage --filter-type '!funccov' --annotate report coverage.dat
.. _line coverage:

View File

@ -70,6 +70,7 @@ set(HEADERS
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Covergroup.h
V3Dead.h
V3Delayed.h
V3Depth.h
@ -235,6 +236,7 @@ set(COMMON_SOURCES
V3Const__gen.cpp
V3Coverage.cpp
V3CoverageJoin.cpp
V3Covergroup.cpp
V3Dead.cpp
V3Delayed.cpp
V3Depth.cpp

View File

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

View File

@ -619,11 +619,236 @@ public:
~ActiveVisitor() override = default;
};
//######################################################################
// Automatic covergroup sampling visitor
// This runs after ActiveVisitor to add automatic sample() calls for covergroups
// declared with sensitivity events (e.g., covergroup cg @(posedge clk);)
class CovergroupSamplingVisitor final : public VNVisitor {
// STATE
ActiveNamer m_namer; // Reuse active naming infrastructure
AstScope* m_scopep = nullptr; // Current scope
bool m_inFirstPass = true; // First pass collects CFuncs, second pass adds sampling
std::unordered_map<const AstClass*, AstCFunc*>
m_covergroupSampleFuncs; // Class -> sample CFunc
// Helper to get the clocking event from a covergroup class
AstSenTree* getCovergroupEvent(AstClass* classp) {
// The AstCovergroup (holding the SenTree) was left in membersp by V3Covergroup
for (AstNode* memberp = classp->membersp(); memberp; memberp = memberp->nextp()) {
if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) {
if (cgp->eventp()) return cgp->eventp();
}
}
return nullptr;
}
// VISITORS
void visit(AstScope* nodep) override {
m_scopep = nodep;
m_namer.main(nodep); // Initialize active naming for this scope
// First pass: collect sample CFuncs from covergroup class scopes
if (m_inFirstPass) {
// Check if this is a covergroup class scope (contains sample CFunc)
for (AstNode* itemp = m_scopep->blocksp(); itemp; itemp = itemp->nextp()) {
if (AstCFunc* const cfuncp = VN_CAST(itemp, CFunc)) {
if (cfuncp->name().find("sample") != string::npos) {
// This is a covergroup class scope - find the class and store the CFunc
// The scope name is like "TOP.t__03a__03acg", extract class name
string scopeName = nodep->name();
size_t dotPos = scopeName.find('.');
if (dotPos != string::npos) {
string className = scopeName.substr(dotPos + 1);
// Search netlist for the matching covergroup class
for (AstNode* modp = v3Global.rootp()->modulesp(); modp;
modp = modp->nextp()) {
if (AstClass* const classp = VN_CAST(modp, Class)) {
if (classp->isCovergroup() && classp->name() == className) {
m_covergroupSampleFuncs[classp] = cfuncp;
cfuncp->isCovergroupSample(true);
break;
}
}
}
}
break;
}
}
}
}
iterateChildren(nodep);
m_scopep = nullptr;
}
void visit(AstVarScope* nodep) override {
// Only process VarScopes in the second pass
if (m_inFirstPass) return;
// Get the underlying var
AstVar* const varp = nodep->varp();
if (!varp) return;
// Check if the variable is of covergroup class type
const AstNodeDType* const dtypep = varp->dtypep();
if (!dtypep) return;
const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType);
if (!classRefp) return;
AstClass* const classp = classRefp->classp();
if (!classp || !classp->isCovergroup()) return;
// Check if this covergroup has an automatic sampling event
AstSenTree* const eventp = getCovergroupEvent(classp);
if (!eventp) return; // No automatic sampling for this covergroup
// Get the sample CFunc - we need to find it in the class scope
// The class scope name is like "TOP.t__03a__03acg" for class "t__03a__03acg"
const string classScopeName = string("TOP.") + classp->name();
AstCFunc* sampleCFuncp = nullptr;
// Search through all scopes to find the class scope and its sample CFunc
for (AstNode* scopeNode = m_scopep; scopeNode; scopeNode = scopeNode->backp()) {
if (AstNetlist* netlistp = VN_CAST(scopeNode, Netlist)) {
// Found netlist, search its modules for scopes
for (AstNode* modp = netlistp->modulesp(); modp; modp = modp->nextp()) {
if (AstScope* scopep = VN_CAST(modp, Scope)) {
if (scopep->name() == classScopeName) {
// Found the class scope, now find the sample CFunc
for (AstNode* itemp = scopep->blocksp(); itemp;
itemp = itemp->nextp()) {
if (AstCFunc* cfuncp = VN_CAST(itemp, CFunc)) {
if (cfuncp->name().find("sample") != string::npos) {
sampleCFuncp = cfuncp;
break;
}
}
}
break;
}
}
}
break;
}
}
if (!sampleCFuncp) {
// Fallback: try the cached version
auto it = m_covergroupSampleFuncs.find(classp);
if (it != m_covergroupSampleFuncs.end()) { sampleCFuncp = it->second; }
}
if (!sampleCFuncp) {
UINFO(4, "Could not find sample() CFunc for covergroup " << classp->name() << endl);
return; // CFunc not found
}
UASSERT_OBJ(sampleCFuncp, nodep, "Sample CFunc is null for covergroup");
// 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};
// Set dtype to void since sample() doesn't return a value
cmethodCallp->dtypeSetVoid();
// Set argTypes to "vlSymsp" so the emit code will pass it automatically
cmethodCallp->argTypes("vlSymsp");
// Clone the sensitivity for this active block
// Each VarRef in the sensitivity needs to be updated for the current scope
AstSenTree* senTreep = eventp->cloneTree(false);
// Fix up VarRefs in the cloned sensitivity - they need varScopep set
senTreep->foreach([this](AstVarRef* refp) {
if (!refp->varScopep() && refp->varp()) {
// Find the VarScope for this Var in the current scope
AstVarScope* vscp = nullptr;
for (AstNode* itemp = m_scopep->varsp(); itemp; itemp = itemp->nextp()) {
if (AstVarScope* const vsp = VN_CAST(itemp, VarScope)) {
if (vsp->varp() == refp->varp()) {
vscp = vsp;
break;
}
}
}
if (vscp) {
refp->varScopep(vscp);
UINFO(4, "Fixed VarRef in SenTree: " << refp->varp()->name() << " -> "
<< vscp->name() << endl);
} else {
refp->v3fatalSrc("Could not find VarScope for clock signal '"
<< refp->varp()->name() << "' in scope " << m_scopep->name()
<< " when creating covergroup sampling active");
}
}
});
// Get or create the AstActive node for this sensitivity
// senTreep is a template used by getActive() which clones it into the AstActive;
// delete it afterwards as it is not added to the AST directly.
AstActive* const activep = m_namer.getActive(fl, senTreep);
VL_DO_DANGLING(senTreep->deleteTree(), senTreep);
// Add the CMethodCall statement to the active domain
activep->addStmtsp(cmethodCallp->makeStmt());
UINFO(4, " Added automatic sample() call for covergroup " << varp->name() << endl);
}
void visit(AstActive*) override {} // Don't iterate into actives
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CovergroupSamplingVisitor(AstNetlist* nodep) {
// NOTE: Automatic sampling now works with --timing
// Previously disabled due to compatibility issues with V3Timing transformations
// The current implementation injects sampling before V3Active, allowing both modes to work
UINFO(4, "CovergroupSamplingVisitor: Starting" << endl);
// First pass: collect sample CFuncs from covergroup class scopes
m_inFirstPass = true;
iterate(nodep);
// Second pass: add automatic sampling to covergroup instances
m_inFirstPass = false;
iterate(nodep);
UINFO(4, "CovergroupSamplingVisitor: Complete" << endl);
}
~CovergroupSamplingVisitor() override = default;
};
//######################################################################
// Active class functions
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ ActiveVisitor{nodep}; } // Destruct before checking
{ CovergroupSamplingVisitor{nodep}; } // Add automatic covergroup sampling
// Delete AstCovergroup nodes (event holders) left in covergroup classes by
// V3CoverageFunctional. They were kept in the AST to avoid orphaned SenTree nodes;
// now that V3Active has consumed them we can delete them.
for (AstNode* modp = nodep->modulesp(); modp; modp = modp->nextp()) {
if (AstClass* const classp = VN_CAST(modp, Class)) {
if (!classp->isCovergroup()) continue;
for (AstNode* memberp = classp->membersp(); memberp;) {
AstNode* const nextp = memberp->nextp();
if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) {
cgp->unlinkFrBack();
VL_DO_DANGLING(cgp->deleteTree(), cgp);
}
memberp = nextp;
}
}
}
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -1052,6 +1052,113 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
//######################################################################
class VCoverBinsType final {
public:
enum en : uint8_t {
USER,
ARRAY,
AUTO,
BINS_IGNORE, // Renamed to avoid Windows macro conflict
BINS_ILLEGAL, // Renamed to avoid Windows macro conflict
DEFAULT,
BINS_WILDCARD, // Renamed to avoid Windows macro conflict
TRANSITION
};
enum en m_e;
VCoverBinsType()
: m_e{USER} {}
// cppcheck-suppress noExplicitConstructor
constexpr VCoverBinsType(en _e)
: m_e{_e} {}
explicit VCoverBinsType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[]
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
return names[m_e];
}
};
constexpr bool operator==(const VCoverBinsType& lhs, const VCoverBinsType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VCoverBinsType::en lhs, const VCoverBinsType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
class VCoverOptionType final {
public:
enum en : uint8_t { WEIGHT, GOAL, AT_LEAST, AUTO_BIN_MAX, PER_INSTANCE, COMMENT };
enum en m_e;
VCoverOptionType()
: m_e{WEIGHT} {}
// cppcheck-suppress noExplicitConstructor
constexpr VCoverOptionType(en _e)
: m_e{_e} {}
explicit VCoverOptionType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[]
= {"weight", "goal", "at_least", "auto_bin_max", "per_instance", "comment"};
return names[m_e];
}
};
constexpr bool operator==(const VCoverOptionType& lhs, const VCoverOptionType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VCoverOptionType::en lhs, const VCoverOptionType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
class VTransRepType final {
public:
enum en : uint8_t {
NONE, // No repetition
CONSEC, // Consecutive repetition [*]
GOTO, // Goto repetition [->]
NONCONS // Nonconsecutive repetition [=]
};
enum en m_e;
VTransRepType()
: m_e{NONE} {}
// cppcheck-suppress noExplicitConstructor
constexpr VTransRepType(en _e)
: m_e{_e} {}
explicit VTransRepType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
return names[m_e];
}
const char* asciiJson() const {
static const char* const names[] = {"", "\"consec\"", "\"goto\"", "\"noncons\""};
return names[m_e];
}
};
constexpr bool operator==(const VTransRepType& lhs, const VTransRepType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VTransRepType& lhs, VTransRepType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VTransRepType::en lhs, const VTransRepType& rhs) {
return lhs == rhs.m_e;
}
//######################################################################
class VDirection final {
public:
enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF };

View File

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

View File

@ -250,6 +250,20 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode {
// Base class for functional coverage items (coverpoints, crosses)
protected:
string m_name;
public:
AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name)
: AstNode{t, fl}
, m_name{name} {}
ASTGEN_MEMBERS_AstNodeFuncCovItem;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; }
bool maybePointedTo() const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
@ -506,6 +520,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 = "")
@ -536,6 +551,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;
@ -611,6 +627,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 {
@ -1013,6 +1031,175 @@ public:
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
// Forward declarations for types used in constructors below
class AstCoverTransSet;
class AstCoverSelectExpr;
class AstCoverBin final : public AstNode {
// @astgen op1 := rangesp : List[AstNode]
// @astgen op2 := iffp : Optional[AstNodeExpr]
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
// @astgen op4 := transp : List[AstCoverTransSet]
string m_name;
VCoverBinsType m_type;
bool m_isArray = false;
public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isWildcard ? VCoverBinsType::BINS_WILDCARD
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: VCoverBinsType::USER))} {
if (rangesp) addRangesp(rangesp);
}
// Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{VCoverBinsType::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, bool isIgnore,
bool isIllegal, bool isArrayBin = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE : VCoverBinsType::TRANSITION)}
, m_isArray{isArrayBin} {
if (transp) addTransp(transp);
}
ASTGEN_MEMBERS_AstCoverBin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
VCoverBinsType binsType() const { return m_type; }
bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; }
};
class AstCoverCrossBins final : public AstNode {
// @astgen op1 := selectp : Optional[AstCoverSelectExpr]
string m_name;
public:
AstCoverCrossBins(FileLine* fl, const string& name, AstCoverSelectExpr* selectp)
: ASTGEN_SUPER_CoverCrossBins(fl)
, m_name{name} {
this->selectp(selectp);
}
ASTGEN_MEMBERS_AstCoverCrossBins;
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 AstCoverOption final : public AstNode {
// @astgen op1 := valuep : AstNodeExpr
VCoverOptionType m_type;
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 AstCoverSelectExpr final : public AstNode {
// @astgen op1 := exprp : AstNodeExpr
public:
AstCoverSelectExpr(FileLine* fl, AstNodeExpr* exprp)
: ASTGEN_SUPER_CoverSelectExpr(fl) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverSelectExpr;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
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]
VTransRepType m_repType;
public:
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
: ASTGEN_SUPER_CoverTransItem(fl)
, m_repType{repType} {
if (valuesp) addValuesp(valuesp);
}
ASTGEN_MEMBERS_AstCoverTransItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VTransRepType repType() const { return m_repType; }
};
class AstCoverTransSet final : public AstNode {
// Represents a transition set: value1 => value2 => value3
// @astgen op1 := itemsp : List[AstCoverTransItem]
public:
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
: ASTGEN_SUPER_CoverTransSet(fl) {
if (itemsp) addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverTransSet;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCovergroup final : public AstNode {
// @astgen op1 := argsp : List[AstVar]
// @astgen op2 := membersp : List[AstNode]
// @astgen op3 := eventp : Optional[AstSenTree]
// @astgen op4 := sampleArgsp : List[AstVar]
string m_name;
bool m_isClass = false;
public:
AstCovergroup(FileLine* fl, const string& name, AstVar* argsp, AstVar* sampleArgsp,
AstNode* membersp, AstSenTree* eventp)
: ASTGEN_SUPER_Covergroup(fl)
, m_name{name} {
if (argsp) addArgsp(argsp);
if (sampleArgsp) addSampleArgsp(sampleArgsp);
if (membersp) addMembersp(membersp);
this->eventp(eventp);
}
ASTGEN_MEMBERS_AstCovergroup;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isClass() const { return m_isClass; }
void isClass(bool flag) { m_isClass = flag; }
bool maybePointedTo() const override { return true; }
};
class AstCoverpointRef final : public AstNode {
// @astgen ptr := m_coverpointp : Optional[AstCoverpoint]
string m_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; }
AstCoverpoint* coverpointp() const { return m_coverpointp; }
void coverpointp(AstCoverpoint* nodep) { m_coverpointp = nodep; }
};
class AstDefParam final : public AstNode {
// A defparam assignment
// Parents: MODULE
@ -2495,6 +2682,33 @@ 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 := binsp : List[AstCoverCrossBins]
// @astgen op3 := optionsp : List[AstCoverOption]
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
: ASTGEN_SUPER_CoverCross(fl, name) {
if (itemsp) 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[AstCoverBin]
// @astgen op3 := iffp : Optional[AstNodeExpr]
// @astgen op4 := optionsp : List[AstCoverOption]
public:
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp)
: ASTGEN_SUPER_Coverpoint(fl, name) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverpoint;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
// === AstNodeGen ===
class AstGenBlock final : public AstNodeGen {
@ -2577,6 +2791,8 @@ class AstClass final : public AstNodeModule {
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
// Covergroup options (when m_covergroup is true)
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
public:
AstClass(FileLine* fl, const string& name, const string& libname)
@ -2604,6 +2820,9 @@ public:
void needRNG(bool flag) { m_needRNG = flag; }
bool useVirtualPublic() const { return m_useVirtualPublic; }
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
// Covergroup options accessors
int cgAutoBinMax() const { return m_cgAutoBinMax; }
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
// Return true if this class is an extension of base class (SLOW)
// Accepts nullptrs
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);

View File

@ -3443,3 +3443,89 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
//######################################################################
// Functional coverage dump methods
void AstCovergroup::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
if (m_isClass) str << " [class]";
}
void AstCovergroup::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isClass);
dumpJsonGen(str);
}
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_name << " " << m_type.ascii();
if (m_isArray) str << "[]";
}
void AstCoverBin::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
if (m_isArray) str << ", \"isArray\": true";
}
void AstCoverTransItem::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (m_repType != VTransRepType::NONE) str << " " << m_repType.ascii();
}
void AstCoverTransItem::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
if (m_repType != VTransRepType::NONE) { str << ", \"repType\": " << m_repType.asciiJson(); }
}
void AstCoverTransSet::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " trans_set";
}
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 AstCoverCrossBins::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
}
void AstCoverCrossBins::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
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);
str << " " << m_name;
}
void AstCoverpointRef::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
void AstCoverSelectExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstCoverSelectExpr::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }

View File

@ -797,7 +797,6 @@ class CoverageVisitor final : public VNVisitor {
pair.first->second = varp;
if (m_ftaskp) {
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
} else {
m_modp->stmtsp()->addHereThisAsNext(varp);

1896
src/V3Covergroup.cpp Normal file

File diff suppressed because it is too large Load Diff

30
src/V3Covergroup.h Normal file
View File

@ -0,0 +1,30 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Covergroup implementation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3COVERGROUP_H_
#define VERILATOR_V3COVERGROUP_H_
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
class V3Covergroup final {
public:
static void covergroup(AstNetlist* nodep);
};
#endif // Guard

View File

@ -227,6 +227,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
void visit(AstAssignDly* nodep) override {
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " <= ");
iterateAndNextConstNull(nodep->rhsp());
puts(";\n");
}
void visit(AstAlias* nodep) override {
putbs("alias ");
iterateConst(nodep->itemsp());
@ -267,7 +273,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
void visit(AstCReset* nodep) override { puts("/*CRESET*/"); }
void visit(AstCase* nodep) override {
putfs(nodep, "");
if (nodep->priorityPragma()) puts("priority ");

View File

@ -130,6 +130,7 @@ class V3Global final {
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
bool m_useParallelBuild = false; // Use parallel build for model
bool m_useRandSequence = false; // Has `randsequence`
bool m_useCovergroup = false; // Has covergroup declarations
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
@ -213,6 +214,8 @@ public:
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useRandSequence() const { return m_useRandSequence; }
void useRandSequence(bool flag) { m_useRandSequence = flag; }
bool useCovergroup() const { return m_useCovergroup; }
void useCovergroup(bool flag) { m_useCovergroup = flag; }
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
void saveJsonPtrFieldName(const std::string& fieldName);

View File

@ -308,7 +308,6 @@ class LinkIncVisitor final : public VNVisitor {
AstVar* const varp = new AstVar{
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
if (m_ftaskp) varp->funcLocal(true);
// Declare the variable

View File

@ -1107,6 +1107,216 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
// Create boilerplate covergroup methods on the given AstClass.
// argsp/sampleArgsp are the raw arg lists still owned by the caller; they are iterated
// (cloned) but not deleted here.
static void createCovergroupMethods(AstClass* nodep, AstNode* argsp, AstNode* sampleArgsp) {
// Hidden static to take unspecified reference argument results
AstVar* const defaultVarp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
nodep->addStmtsp(defaultVarp);
// Handle constructor arguments - add function parameters and assignments
if (argsp) {
// Find the 'new' function to add parameters to
AstFunc* newFuncp = nullptr;
for (AstNode* memberp = nodep->membersp(); memberp; memberp = memberp->nextp()) {
if (AstFunc* const funcp = VN_CAST(memberp, Func)) {
if (funcp->name() == "new") {
newFuncp = funcp;
break;
}
}
}
if (newFuncp) {
// Save the existing body statements and unlink them
AstNode* const existingBodyp = newFuncp->stmtsp();
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
// Add function parameters and assignments
for (AstNode* argp = argsp; argp; argp = argp->nextp()) {
if (AstVar* const origVarp = VN_CAST(argp, Var)) {
AstVar* const paramp = origVarp->cloneTree(false);
paramp->funcLocal(true);
paramp->direction(VDirection::INPUT);
newFuncp->addStmtsp(paramp);
AstNodeExpr* const lhsp
= new AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp
= new AstParseRef{paramp->fileline(), paramp->name()};
newFuncp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
}
if (existingBodyp) newFuncp->addStmtsp(existingBodyp);
}
}
// IEEE: option
{
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([arguments])
{
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
if (sampleArgsp) {
for (AstNode* argp = sampleArgsp; argp; argp = argp->nextp()) {
if (AstVar* const origVarp = VN_CAST(argp, Var)) {
AstVar* const paramp = origVarp->cloneTree(false);
paramp->funcLocal(true);
paramp->direction(VDirection::INPUT);
funcp->addStmtsp(paramp);
AstNodeExpr* const lhsp
= new AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp
= new AstParseRef{paramp->fileline(), paramp->name()};
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
}
}
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: function void start(), void stop()
for (const string& name : {"start"s, "stop"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: static function real get_coverage(optional ref int, optional ref int)
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
funcp->isStatic(name == "get_coverage");
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
{
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
nodep->findDoubleDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::OUTPUT);
varp->funcReturn(true);
funcp->fvarp(varp);
}
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
funcp->addStmtsp(varp);
}
}
// IEEE: function void set_inst_name(string)
{
AstFunc* const funcp
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
funcp->addStmtsp(varp);
}
}
void visit(AstCovergroup* nodep) override {
// If we're already inside a covergroup class, this is the sentinel AstCovergroup
// node carrying the clocking event for V3Covergroup - don't re-transform it.
if (m_modp && VN_IS(m_modp, Class) && VN_CAST(m_modp, Class)->isCovergroup()) return;
// Transform raw parse-time AstCovergroup into a fully-formed AstClass
cleanFileline(nodep);
const string libname = m_modp ? m_modp->libname() : "";
AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname};
cgClassp->isCovergroup(true);
v3Global.useCovergroup(true);
// Clocking event: unlink before deleteTree, attach as AstCovergroup child on class
if (AstSenTree* const eventp = nodep->eventp()) {
eventp->unlinkFrBack();
AstCovergroup* const cgNodep = new AstCovergroup{
nodep->fileline(), nodep->name(), nullptr, nullptr, nullptr, eventp};
cgClassp->addMembersp(cgNodep);
}
// Convert constructor args to member variables
for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) {
if (AstVar* const origVarp = VN_CAST(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()) {
if (AstVar* const origVarp = VN_CAST(argp, Var)) {
AstVar* const memberp = origVarp->cloneTree(false);
memberp->varType(VVarType::MEMBER);
memberp->funcLocal(false);
memberp->direction(VDirection::NONE);
cgClassp->addMembersp(memberp);
}
}
// Create the constructor; detach membersp (coverage body) and use as its body
{
AstFunc* const newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
newp->classMethod(true);
newp->isConstructor(true);
newp->dtypep(cgClassp->dtypep());
if (AstNode* const bodyp = nodep->membersp()) {
bodyp->unlinkFrBackWithNext();
newp->addStmtsp(bodyp);
}
cgClassp->addMembersp(newp);
}
// Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep)
createCovergroupMethods(cgClassp, nodep->argsp(), nodep->sampleArgsp());
// Replace AstCovergroup with AstClass and process the new class normally
nodep->replaceWith(cgClassp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
iterate(cgClassp);
}
void visit(AstNode* nodep) override {
// Default: Just iterate
cleanFileline(nodep);

View File

@ -250,6 +250,10 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst {
void analyzeVarRef(AstVarRef* nodep) {
const VAccess access = nodep->access();
AstVar* const varp = nodep->varp();
// Add null check - varp can be null in some contexts (e.g., SenTree VarRefs)
if (!varp) return;
// Skip if not in a statement context (m_propsp can be null)
if (!m_propsp) return;
// Gather read and written variables
if (access.isReadOrRW()) m_propsp->m_rdVars.insert(varp);
if (access.isWriteOrRW()) m_propsp->m_wrVars.insert(varp);
@ -295,7 +299,6 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst {
iterateChildrenConst(nodep);
}
// VISITORS
void visit(AstNode* nodep) override {
// Push a new stack entry at the start of a list, but only if the list is not a
// single element (this saves a lot of allocations in expressions)

View File

@ -331,6 +331,16 @@ class OrderGraphBuilder final : public VNVisitor {
void visit(AstCoverToggle* nodep) override { //
iterateLogic(nodep);
}
void visit(AstStmtExpr* nodep) override {
// StmtExpr wraps expressions used as statements (e.g., method calls).
// If it's under an AstActive but not already in a logic context, treat it as logic.
// Otherwise just iterate normally.
if (!m_logicVxp && m_domainp) {
iterateLogic(nodep);
} else {
iterateChildren(nodep);
}
}
//--- Ignored nodes
void visit(AstVar*) override {}

View File

@ -15,11 +15,13 @@
//*************************************************************************
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Control.h"
#include "V3Global.h"
#include "V3ParseImp.h" // Defines YYTYPE; before including bison header
#include <stack>
#include <vector>
class V3ParseGrammar final {
public:
@ -92,96 +94,65 @@ 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);
// Helper to move bins from parser list to coverpoint
void addCoverpointBins(AstCoverpoint* cp, AstNode* binsList) {
if (!binsList) return;
// 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);
}
// CRITICAL FIX: The parser creates a linked list of bins. When we try to move them
// to the coverpoint one by one while they're still linked, the addNext() logic
// that updates headtailp pointers creates circular references. We must fully
// unlink ALL bins before adding ANY to the coverpoint.
std::vector<AstCoverBin*> bins;
std::vector<AstCoverOption*> options;
// 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);
}
// To unlink the head node (which has no backp), create a temporary parent
AstBegin* tempParent = new AstBegin{binsList->fileline(), "[TEMP]", nullptr, true};
tempParent->addStmtsp(binsList); // Now binsList has a backp
// 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);
}
// Now unlink all bins - they all have backp now
for (AstNode *binp = binsList, *nextp; binp; binp = nextp) {
nextp = binp->nextp();
// 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);
if (AstCoverBin* cbinp = VN_CAST(binp, CoverBin)) {
cbinp->unlinkFrBack(); // Now this works for all bins including head
bins.push_back(cbinp);
} else if (AstCgOptionAssign* optp = VN_CAST(binp, CgOptionAssign)) {
optp->unlinkFrBack();
// Convert AstCgOptionAssign to AstCoverOption
VCoverOptionType optType = VCoverOptionType::COMMENT; // default
if (optp->name() == "at_least") {
optType = VCoverOptionType::AT_LEAST;
} else if (optp->name() == "weight") {
optType = VCoverOptionType::WEIGHT;
} else if (optp->name() == "goal") {
optType = VCoverOptionType::GOAL;
} else if (optp->name() == "auto_bin_max") {
optType = VCoverOptionType::AUTO_BIN_MAX;
} else if (optp->name() == "per_instance") {
optType = VCoverOptionType::PER_INSTANCE;
} else if (optp->name() == "comment") {
optType = VCoverOptionType::COMMENT;
} else {
optp->v3warn(COVERIGN,
"Ignoring unsupported coverage option: " + optp->name());
}
AstCoverOption* coverOptp = new AstCoverOption{optp->fileline(), optType,
optp->valuep()->cloneTree(false)};
options.push_back(coverOptp);
VL_DO_DANGLING(optp->deleteTree(), optp);
} else {
binp->v3warn(COVERIGN,
"Unexpected node in bins list, ignoring"); // LCOV_EXCL_LINE
VL_DO_DANGLING(binp->deleteTree(), binp);
}
}
// 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);
}
// Delete the temporary parent
VL_DO_DANGLING(tempParent->deleteTree(), tempParent);
// Now add standalone bins and options to coverpoint
for (AstCoverBin* cbinp : bins) { cp->addBinsp(cbinp); }
for (AstCoverOption* optp : options) { cp->addOptionsp(optp); }
}
AstDisplay* createDisplayError(FileLine* fileline) {
AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr};

View File

@ -240,6 +240,9 @@ class SchedGraphBuilder final : public VNVisitor {
void visit(AstNodeProcedure* nodep) override { visitLogic(nodep); }
void visit(AstNodeAssign* nodep) override { visitLogic(nodep); }
void visit(AstCoverToggle* nodep) override { visitLogic(nodep); }
void visit(AstStmtExpr* nodep) override {
visitLogic(nodep);
} // Handle statement expressions like method calls
// Pre and Post logic are handled separately
void visit(AstAlwaysPre* nodep) override {}

View File

@ -343,11 +343,30 @@ class TimingSuspendableVisitor final : public VNVisitor {
}
}
void visit(AstNodeCCall* nodep) override {
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()), getSuspendDepVtx(m_procp),
P_CALL};
// Skip automatic covergroup sampling calls (marked with user3==1)
if (nodep->user3()) {
iterateChildren(nodep);
return;
}
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
getNeedsProcDepVtx(m_procp), P_CALL};
AstCFunc* funcp = nodep->funcp();
if (!funcp) {
iterateChildren(nodep);
return;
}
// Skip if we're not inside a function/procedure (m_procp would be null)
// This can happen for calls in Active nodes at module scope
if (!m_procp) {
iterateChildren(nodep);
return;
}
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);
}
@ -914,8 +933,16 @@ class TimingControlVisitor final : public VNVisitor {
}
}
void visit(AstNodeCCall* nodep) override {
if (nodep->funcp()->needProcess()) m_hasProcess = true;
if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
AstCFunc* const funcp = nodep->funcp();
// Skip automatic covergroup sampling calls
if (funcp->isCovergroupSample()) {
iterateChildren(nodep);
return;
}
if (funcp->needProcess()) m_hasProcess = true;
if (hasFlags(funcp, T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
// Calls to suspendables are always void return type, hence parent must be StmtExpr
AstStmtExpr* const stmtp = VN_AS(nodep->backp(), StmtExpr);
stmtp->replaceWith(new AstCAwait{nodep->fileline(), nodep->unlinkFrBack()});

View File

@ -1699,8 +1699,32 @@ 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
// Find parent covergroup (AstClass with isCovergroup() == true)
AstClass* cgClassp = nullptr;
for (AstNode* parentp = nodep->backp(); parentp; parentp = parentp->backp()) {
if (AstClass* classp = VN_CAST(parentp, Class)) {
if (classp->isCovergroup()) {
cgClassp = classp;
break;
}
}
}
if (cgClassp) {
// Process supported options
if (nodep->name() == "auto_bin_max" && !nodep->typeOption()) {
// Extract constant value
if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) {
cgClassp->cgAutoBinMax(constp->toSInt());
UINFO(6, " Covergroup " << cgClassp->name() << " option.auto_bin_max = "
<< constp->toSInt() << endl);
}
}
// Add more options here as needed (weight, goal, at_least, per_instance, comment)
}
// Delete the assignment node (we've extracted the value)
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstPow* nodep) override {
@ -3320,6 +3344,9 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstInsideRange* nodep) override {
// Just do each side; AstInside will rip these nodes out later
// Constant-fold range bounds (e.g., NEGATE(100) becomes -100)
V3Const::constifyParamsEdit(nodep->lhsp()); // May relink pointed to node
V3Const::constifyParamsEdit(nodep->rhsp()); // May relink pointed to node
userIterateAndNext(nodep->lhsp(), m_vup);
userIterateAndNext(nodep->rhsp(), m_vup);
nodep->dtypeFrom(nodep->lhsp());

View File

@ -38,6 +38,7 @@
#include "V3Control.h"
#include "V3Coverage.h"
#include "V3CoverageJoin.h"
#include "V3Covergroup.h"
#include "V3Dead.h"
#include "V3Delayed.h"
#include "V3Depth.h"
@ -156,7 +157,10 @@ static void process() {
}
// Convert parseref's to varrefs, and other directly post parsing fixups
// Note: must run before removeStd() as it may create std:: references (e.g. covergroups)
V3LinkParse::linkParse(v3Global.rootp());
// Remove std package if unused (must be after V3LinkParse which may set usesStdPackage)
v3Global.removeStd();
// Cross-link signal names
// Cross-link dotted hierarchical references
V3LinkDot::linkDotPrimary(v3Global.rootp());
@ -230,6 +234,10 @@ static void process() {
// Before we do dead code elimination and inlining, or we'll lose it.
if (v3Global.opt.coverage()) V3Coverage::coverage(v3Global.rootp());
// Functional coverage code generation
// Generate code for covergroups/coverpoints
if (v3Global.useCovergroup()) V3Covergroup::covergroup(v3Global.rootp());
// Resolve randsequence if they are used by the design
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());
@ -737,7 +745,6 @@ static bool verilate(const string& argString) {
// Read first filename
v3Global.readFiles();
v3Global.removeStd();
// Link, etc, if needed
if (!v3Global.opt.preprocOnly()) { //

View File

@ -6895,40 +6895,23 @@ 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};
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");
}
{ BBCOVERIGN($1, "Ignoring unsupported: covergroup inheritance (extends)");
$$ = new AstCovergroup{$<fl>3, *$3, nullptr, nullptr, $5, nullptr};
GRAMMARP->endLabel($<fl>7, $$, $7); }
;
cgPortListE<nodep>:
@ -6975,21 +6958,46 @@ 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); }
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>1, "", $2};
if ($3) cp->iffp(VN_AS($3, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $4);
$$ = cp; }
// // 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);}
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>3, *$1, $4};
if ($5) cp->iffp(VN_AS($5, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $6);
$$ = cp; }
// // 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);}
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>4, *$2, $5};
if ($6) cp->iffp(VN_AS($6, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $7);
$$ = cp;
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); }
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>5, *$3, $6};
if ($7) cp->iffp(VN_AS($7, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $8);
$$ = cp;
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); }
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>5, *$3, $6};
if ($7) cp->iffp(VN_AS($7, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $8);
$$ = cp;
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); }
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>5, *$3, $6};
if ($7) cp->iffp(VN_AS($7, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $8);
$$ = cp;
DEL($2); }
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); }
{ AstCoverpoint* const cp = new AstCoverpoint{$<fl>4, *$2, $5};
if ($6) cp->iffp(VN_AS($6, NodeExpr));
GRAMMARP->addCoverpointBins(cp, $7);
$$ = cp; }
// // IEEE-2012:
| bins_or_empty { $$ = $1; }
;
@ -6997,7 +7005,7 @@ cover_point<nodep>: // ==IEEE: cover_point
iffE<nodep>: // 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
@ -7021,55 +7029,127 @@ 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
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, false, false};
if ($3) VN_AS($$, CoverBin)->isArray(true); // If bins_orBraE returned non-null, it's array
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, "Ignoring unsupported: bin array (non-auto)");
DEL($4, $6);
}
}
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, true, false};
if ($3) VN_AS($$, CoverBin)->isArray(true);
DEL($8); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, false, true};
if ($3) VN_AS($$, CoverBin)->isArray(true);
DEL($8); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, false, false};
DEL($10, $12); /* TODO: Support 'with' clause */ }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, true, false};
DEL($10, $12); /* TODO: Support 'with' clause */ }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, $6, false, true};
DEL($10, $12); /* TODO: Support 'with' clause */ }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($8, $10); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($8, $10); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); 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, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($7, $11, $13); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($7, $11, $13); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); 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), false, false, 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), true, false, 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), false, true, isArray != nullptr};
DEL($6); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($6, $7);}
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($6, $7);}
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); 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::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, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($7); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($7); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); 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*/ }
| '[' ']' { $$ = $<fl>1; /* Mark as array */ }
| '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); }
;
bins_keyword<fl>: // ==IEEE: bins_keyword
yBINS { $$ = $1; /*UNSUP*/ }
| yILLEGAL_BINS { $$ = $1; /*UNSUP*/ }
| yIGNORE_BINS { $$ = $1; /*UNSUP*/ }
;
trans_list<nodep>: // ==IEEE: trans_list
'(' trans_set ')' { $$ = $2; }
| 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 {
// Single transition item - wrap in AstCoverTransSet
$$ = new AstCoverTransSet{$<fl>1, static_cast<AstCoverTransItem*>($1)};
}
| trans_set yP_EQGT trans_range_list
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); }
{
// Chain transition items with => operator
// Add new item to existing set
$$ = $1;
static_cast<AstCoverTransSet*>($$)->addItemsp(static_cast<AstCoverTransItem*>($3));
}
;
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); }
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
@ -7084,7 +7164,7 @@ trans_range_list<nodep>: // ==IEEE: trans_range_list
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); }
;
trans_item<nodep>: // ==IEEE: range_list
trans_item<nodep>: // ==IEEE: range_list (returns range list node)
covergroup_range_list { $$ = $1; }
;
@ -7096,9 +7176,94 @@ 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) { // cross_body items (options, bins)
for (AstNode* itemp = $6; itemp; ) {
AstNode* const nextp = itemp->nextp();
// Helper: unlink itemp from the standalone bison list.
// Head nodes have m_backp==nullptr; use nextp->unlinkFrBackWithNext()
// to detach the rest of the list so itemp->m_nextp becomes null.
const auto unlinkItem = [&]() {
if (itemp->backp()) {
itemp->unlinkFrBack();
} else if (nextp) {
nextp->unlinkFrBackWithNext();
}
};
if (AstCoverOption* optp = VN_CAST(itemp, CoverOption)) {
unlinkItem();
nodep->addOptionsp(optp);
} else if (AstCoverCrossBins* binp = VN_CAST(itemp, CoverCrossBins)) {
unlinkItem();
nodep->addBinsp(binp);
} else if (VN_IS(itemp, CgOptionAssign)) {
unlinkItem();
VL_DO_DANGLING(itemp->deleteTree(), itemp);
} else if (VN_IS(itemp, Func)) {
// Function declarations in cross bodies are unsupported
// Skip them - they will be deleted when bins expressions referencing
// them are deleted via DEL() in the cross_body_item rules
} else {
// Delete other unsupported items
unlinkItem();
VL_DO_DANGLING(itemp->deleteTree(), itemp);
}
itemp = nextp;
}
}
if ($5) {
$5->v3warn(COVERIGN, "Ignoring unsupported: cross iff condition");
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) { // cross_body items (options, bins)
for (AstNode* itemp = $4; itemp; ) {
AstNode* const nextp = itemp->nextp();
// Helper: unlink itemp from the standalone bison list.
// Head nodes have m_backp==nullptr; use nextp->unlinkFrBackWithNext()
// to detach the rest of the list so itemp->m_nextp becomes null.
const auto unlinkItem = [&]() {
if (itemp->backp()) {
itemp->unlinkFrBack();
} else if (nextp) {
nextp->unlinkFrBackWithNext();
}
};
if (AstCoverOption* optp = VN_CAST(itemp, CoverOption)) {
unlinkItem();
nodep->addOptionsp(optp);
} else if (AstCoverCrossBins* binp = VN_CAST(itemp, CoverCrossBins)) {
unlinkItem();
nodep->addBinsp(binp);
} else if (VN_IS(itemp, CgOptionAssign)) {
unlinkItem();
VL_DO_DANGLING(itemp->deleteTree(), itemp);
} else if (VN_IS(itemp, Func)) {
// Function declarations in cross bodies are unsupported
// Skip them - they will be deleted when bins expressions referencing
// them are deleted via DEL() in the cross_body_item rules
} else {
// Delete other unsupported items
unlinkItem();
VL_DO_DANGLING(itemp->deleteTree(), itemp);
}
itemp = nextp;
}
}
if ($3) {
$3->v3warn(COVERIGN, "Ignoring unsupported: cross iff condition");
VL_DO_DANGLING($3->deleteTree(), $3);
}
$$ = nodep;
}
;
list_of_cross_items<nodep>: // ==IEEE: list_of_cross_items
@ -7113,7 +7278,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
@ -7133,12 +7299,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(), "Ignoring unsupported: coverage cross 'function' declaration"); 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, "Ignoring unsupported: explicit coverage cross bins"); DEL($4, $5); }
| yIGNORE_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: explicit coverage cross bins"); DEL($4, $5); }
| yILLEGAL_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: explicit coverage cross bins"); DEL($4, $5); }
| error ';' { $$ = nullptr; } // LCOV_EXCL_LINE
;
@ -7159,7 +7329,7 @@ select_expression_r<nodep>:
| '!' yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($4); }
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($3, $7); }
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($7); }
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($4, $8); }
| yWITH__PAREN '(' cgexpr ')'
@ -7204,7 +7374,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'");

View File

@ -0,0 +1,87 @@
// 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
module t;
/* verilator lint_off UNSIGNED */
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
initial begin
cg cg_inst;
real cov;
cg_inst = new();
// Initial coverage should be 0%
cov = cg_inst.get_inst_coverage();
if (cov != 0.0) begin
$error("Expected 0%% coverage, got %0.2f%%", cov);
end
// Hit first array bin value (1)
data = 1;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After hitting value 1: %0.2f%%", cov);
// 1 bin out of 4 total bins (3 array bins + 1 grouped bin)
if (cov < 23.0 || cov > 27.0) begin
$error("Expected ~25%% (1/4 bins), got %0.2f%%", cov);
end
// Hit second array bin value (5)
data = 5;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After hitting value 5: %0.2f%%", cov);
// 2 bins out of 4
if (cov < 48.0 || cov > 52.0) begin
$error("Expected ~50%% (2/4 bins), got %0.2f%%", cov);
end
// Hit the grouped bin (covers all of 2, 6, 10)
data = 6;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After hitting grouped bin: %0.2f%%", cov);
// 3 bins out of 4
if (cov < 73.0 || cov > 77.0) begin
$error("Expected ~75%% (3/4 bins), got %0.2f%%", cov);
end
// Hit third array bin value (9)
data = 9;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After hitting value 9: %0.2f%%", cov);
// All 4 bins covered
if (cov != 100.0) begin
$error("Expected 100%% (4/4 bins), got %0.2f%%", cov);
end
// Verify hitting other values in grouped bin doesn't increase coverage
data = 2;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
if (cov != 100.0) begin
$error("Coverage should stay 100%%, got %0.2f%%", cov);
end
$display("Array bins test PASSED");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

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

View File

@ -0,0 +1,48 @@
// 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-License-Identifier: CC0-1.0
module t;
/* verilator lint_off UNSIGNED */
/* verilator lint_off CMPCONST */
logic [2:0] data; // 3-bit: 0-7
covergroup cg;
coverpoint data {
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
}
endgroup
/* verilator lint_on CMPCONST */
initial begin
automatic cg cg_inst = new;
// Initial coverage should be 0%
$display("Coverage initial: %f%% (expected ~0.00%%)", cg_inst.get_inst_coverage());
// Sample first bin: 0 or 1
data = 0;
cg_inst.sample();
$display("Coverage after 0: %f%% (expected ~25.00%%)", cg_inst.get_inst_coverage());
// Sample second bin: 2 or 3
data = 2;
cg_inst.sample();
$display("Coverage after 2: %f%% (expected ~50.00%%)", cg_inst.get_inst_coverage());
// Sample third bin: 4 or 5
data = 5;
cg_inst.sample();
$display("Coverage after 5: %f%% (expected ~75.00%%)", cg_inst.get_inst_coverage());
// Sample fourth bin: 6 or 7
data = 7;
cg_inst.sample();
$display("Coverage complete: %f%% (expected ~100.00%%)", cg_inst.get_inst_coverage());
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,28 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
// Simple test harness for t_covergroup_auto_sample - provides clock
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
#include "verilated.h"
#include "Vt_covergroup_auto_sample.h"
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vt_covergroup_auto_sample* top = new Vt_covergroup_auto_sample;
// Run for 20 cycles
for (int i = 0; i < 20; i++) {
top->clk = 0;
top->eval();
top->clk = 1;
top->eval();
if (Verilated::gotFinish()) break;
}
delete top;
return 0;
}

View File

@ -0,0 +1,19 @@
#!/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 automatic sampling with --no-timing (default)
test.scenarios('vlt')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,55 @@
// DESCRIPTION: Verilator: Test automatic sampling with clocking events
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// 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; // Hit bin zero
1: data <= 2'b01; // Hit bin one
2: data <= 2'b10; // Hit bin two
3: data <= 2'b11; // Hit bin three
4: begin
$display("Coverage: %f%%", cg_inst.get_inst_coverage());
if (cg_inst.get_inst_coverage() >= 99.0) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Expected 100%% coverage, got %f%%", cg_inst.get_inst_coverage());
$stop;
end
end
endcase
// NOTE: NO manual .sample() call - relying on automatic sampling!
// Auto-stop after 10 cycles to prevent infinite loop
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

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

View File

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

View File

@ -0,0 +1,122 @@
// 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-License-Identifier: CC0-1.0
// Test automatic bin creation when coverpoint has no explicit bins
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [2:0] data3; // 3-bit: values 0-7
logic [1:0] data2; // 2-bit: values 0-3
// Test 1: auto_bin_max default (64) - should create 8 bins for 3-bit signal
// Each value should get its own bin since 2^3 = 8 < 64
covergroup cg1;
cp_data3: coverpoint data3; // No bins specified - autobins
endgroup
// Test 2: With option.auto_bin_max = 4
// Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
covergroup cg2;
option.auto_bin_max = 4;
cp_data3: coverpoint data3; // No bins specified - autobins
endgroup
// Test 3: With ignore bins - should still auto-create for non-ignored values
// Autobins created, but value 7 is ignored
covergroup cg3;
cp_data3: coverpoint data3 {
ignore_bins reserved = {7};
}
endgroup
// Test 4: Smaller signal - 2-bit
// Should create 4 bins (one per value) since 2^2 = 4 < 64
covergroup cg4;
cp_data2: coverpoint data2; // No bins specified - autobins
endgroup
// Test 5: With auto_bin_max smaller than signal range
// 2-bit signal (0-3) with auto_bin_max=2 should create 2 bins: [0:1], [2:3]
covergroup cg5;
option.auto_bin_max = 2;
cp_data2: coverpoint data2; // No bins specified - autobins
endgroup
initial begin
cg1 cg1_inst;
cg2 cg2_inst;
cg3 cg3_inst;
cg4 cg4_inst;
cg5 cg5_inst;
cg1_inst = new;
cg2_inst = new;
cg3_inst = new;
cg4_inst = new;
cg5_inst = new;
// Test CG1: Hit values 0, 1, 2 (3 of 8 bins = 37.5%)
data3 = 0; cg1_inst.sample();
data3 = 1; cg1_inst.sample();
data3 = 2; cg1_inst.sample();
// Test CG2: Hit values 0, 1, 4 (bins [0:1] and [4:5], 2 of 4 bins = 50%)
data3 = 0; cg2_inst.sample();
data3 = 1; cg2_inst.sample();
data3 = 4; cg2_inst.sample();
// Test CG3: Hit values 0, 1, 7 (7 is ignored, so 2 of 7 valid bins = 28.6%)
data3 = 0; cg3_inst.sample();
data3 = 1; cg3_inst.sample();
data3 = 7; cg3_inst.sample(); // Ignored
// Test CG4: Hit all values 0-3 (4 of 4 bins = 100%)
data2 = 0; cg4_inst.sample();
data2 = 1; cg4_inst.sample();
data2 = 2; cg4_inst.sample();
data2 = 3; cg4_inst.sample();
// Test CG5: Hit values 0, 3 (bins [0:1] and [2:3], 2 of 2 bins = 100%)
data2 = 0; cg5_inst.sample();
data2 = 3; cg5_inst.sample();
$display("CG1 (8 autobins): %0.1f%%", cg1_inst.get_inst_coverage());
$display("CG2 (4 autobins w/ option): %0.1f%%", cg2_inst.get_inst_coverage());
$display("CG3 (7 autobins w/ ignore): %0.1f%%", cg3_inst.get_inst_coverage());
$display("CG4 (4 autobins): %0.1f%%", cg4_inst.get_inst_coverage());
$display("CG5 (2 autobins w/ option): %0.1f%%", cg5_inst.get_inst_coverage());
// Validate coverage results
if (cg1_inst.get_inst_coverage() < 30.0 || cg1_inst.get_inst_coverage() > 45.0) begin
$display("FAIL: CG1 coverage out of range");
$stop;
end
if (cg2_inst.get_inst_coverage() < 45.0 || cg2_inst.get_inst_coverage() > 55.0) begin
$display("FAIL: CG2 coverage should be 50%% (2/4 bins with auto_bin_max=4)");
$stop;
end
if (cg3_inst.get_inst_coverage() < 27.0 || cg3_inst.get_inst_coverage() > 30.0) begin
$display("FAIL: CG3 coverage should be ~28.6%% (2/7 valid bins, value 7 ignored)");
$stop;
end
if (cg4_inst.get_inst_coverage() < 95.0) begin
$display("FAIL: CG4 coverage should be 100%%");
$stop;
end
if (cg5_inst.get_inst_coverage() < 99.0) begin
$display("FAIL: CG5 coverage should be 100%% (2/2 bins with auto_bin_max=2)");
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,14 @@
%Error: t/t_covergroup_autobins_bad.v:17:18: 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:18: Automatic bins array size must be 1-10000, got 0
: ... note: In instance 't'
24 | bins auto[0];
| ^~~~
%Error: t/t_covergroup_autobins_bad.v:31:18: Automatic bins array size must be 1-10000, got 10001
: ... note: In instance 't'
31 | bins auto[10001];
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2025 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=['--error-limit 1000'],
fails=True)
test.passes()

View File

@ -0,0 +1,40 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 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-10000 (zero)
covergroup cg2;
cp1: coverpoint cp_expr {
bins auto[0];
}
endgroup
// Error: array size must be 1-10000 (too large)
covergroup cg3;
cp1: coverpoint cp_expr {
bins auto[10001];
}
endgroup
cg1 cg1_inst = new;
cg2 cg2_inst = new;
cg3 cg3_inst = new;
initial $finish;
endmodule

View File

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

View File

@ -0,0 +1,51 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test viewing individual bin hit counts
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [3:0] data;
covergroup cg;
coverpoint data {
bins zero = {0};
bins low = {[1:3]};
bins mid = {[4:7]};
bins high = {[8:15]};
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
// Sample various values with different frequencies
data = 0; cg_inst.sample(); // zero: 1
data = 1; cg_inst.sample(); // low: 1
data = 2; cg_inst.sample(); // low: 2
data = 2; cg_inst.sample(); // low: 3
data = 5; cg_inst.sample(); // mid: 1
data = 10; cg_inst.sample(); // high: 1
// Verify coverage is 100% (all 4 bins hit)
check_coverage(100.0, "final");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(real expected, string label);
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
if (cov < expected - 0.5 || cov > expected + 0.5) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

@ -0,0 +1,73 @@
// 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 bin options: at_least, weight, goal
module t;
/* verilator lint_off UNSIGNED */
bit [7:0] addr;
covergroup cg;
option.per_instance = 1;
option.comment = "Test covergroup with options";
coverpoint addr {
option.at_least = 2; // Each bin needs at least 2 hits
option.weight = 10; // This coverpoint has weight 10
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:15]};
}
endgroup
initial begin
cg cg_inst;
real cov;
cg_inst = new();
// Hit low once - should be 0% because at_least = 2
addr = 2;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After 1 hit: %0.2f%%", cov);
if (cov != 0.0) begin
$error("Expected 0%% (bin needs 2 hits), got %0.2f%%", cov);
end
// Hit low again - should be 33.33% (1/3 bins)
addr = 1;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After 2 hits to low: %0.2f%%", cov);
if (cov < 30.0 || cov > 35.0) begin
$error("Expected ~33.33%%, got %0.2f%%", cov);
end
// Hit mid twice - should be 66.67% (2/3 bins)
addr = 5; cg_inst.sample();
addr = 6; cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After mid hits: %0.2f%%", cov);
if (cov < 63.0 || cov > 70.0) begin
$error("Expected ~66.67%%, got %0.2f%%", cov);
end
// Hit high twice - should be 100%
addr = 10; cg_inst.sample();
addr = 12; cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After all bins hit: %0.2f%%", cov);
if (cov != 100.0) begin
$error("Expected 100%%, got %0.2f%%", cov);
end
$display("Bin options test PASSED");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,110 @@
// 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-License-Identifier: CC0-1.0
// Test advanced bin types that ARE supported:
// - ignore_bins
// - wildcard bins
// - array bins (explicit values only, not ranges yet)
module t;
logic [3:0] data;
int error_count = 0;
// Test 1: ignore_bins
covergroup cg_ignore;
coverpoint data {
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:11]};
ignore_bins reserved = {[12:15]}; // Should not count toward coverage
}
endgroup
// Test 2: Array bins (with ranges - now working!)
covergroup cg_array;
coverpoint data {
bins values[] = {[0:3]}; // Creates 4 bins: values[0], values[1], values[2], values[3]
}
endgroup
// Test 3: wildcard bins (with don't-care bits)
covergroup cg_wildcard;
coverpoint data {
wildcard bins pattern0 = {4'b00??}; // Matches 0,1,2,3
wildcard bins pattern1 = {4'b01??}; // Matches 4,5,6,7
wildcard bins pattern2 = {4'b10??}; // Matches 8,9,10,11
wildcard bins pattern3 = {4'b11??}; // Matches 12,13,14,15
}
endgroup
initial begin
cg_ignore cg1;
cg_array cg2;
cg_wildcard cg3;
real cov;
cg1 = new;
cg2 = new;
cg3 = new;
// Test 1: ignore_bins
$display("Test 1: ignore_bins");
data = 0; cg1.sample(); // low
data = 5; cg1.sample(); // mid
data = 9; cg1.sample(); // high
data = 12; cg1.sample(); // ignored - should not affect coverage
data = 13; cg1.sample(); // ignored
cov = cg1.get_inst_coverage();
$display(" Coverage with ignore_bins: %0.1f%% (expect 100%%)", cov);
// 3 out of 3 non-ignored bins = 100%
if (cov < 99.0 || cov > 101.0) begin
$display("%%Error: Expected 100%%, got %0.1f%%", cov);
error_count++;
end
// Test 2: Array bins
$display("Test 2: Array bins (with ranges)");
data = 0; cg2.sample(); // values[0]
data = 1; cg2.sample(); // values[1]
data = 2; cg2.sample(); // values[2]
// Note: values[3] not sampled, so 75% coverage expected
cov = cg2.get_inst_coverage();
$display(" Coverage with array bins: %0.1f%% (expect 75%%)", cov);
// 3 out of 4 bins = 75%
if (cov < 74.0 || cov > 76.0) begin
$display("%%Error: Expected 75%%, got %0.1f%%", cov);
error_count++;
end
// Test 3: Wildcard bins
$display("Test 3: Wildcard bins");
data = 2; cg3.sample(); // pattern0 (00??)
data = 5; cg3.sample(); // pattern1 (01??)
data = 10; cg3.sample(); // pattern2 (10??)
// pattern3 not sampled, so 75% coverage
cov = cg3.get_inst_coverage();
$display(" Coverage with wildcard bins: %0.1f%% (expect 75%%)", cov);
// 3 out of 4 bins = 75%
if (cov < 74.0 || cov > 76.0) begin
$display("%%Error: Expected 75%%, got %0.1f%%", cov);
error_count++;
end
if (error_count == 0) begin
$write("*-* All Finished *-*\n");
end else begin
$display("%%Error: %0d test(s) failed", error_count);
$stop;
end
$finish;
end
endmodule

View File

@ -0,0 +1,80 @@
// 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-License-Identifier: CC0-1.0
// Test default bins and illegal_bins
module t;
logic [3:0] data;
int error_count = 0;
// Test 1: default bins
covergroup cg_default;
coverpoint data {
bins special = {0, 5, 10};
bins others = default; // Catch-all for uncovered values
}
endgroup
// Test 2: illegal_bins (we'll test it doesn't crash on valid values)
covergroup cg_valid;
coverpoint data {
bins valid = {[0:10]};
illegal_bins reserved = {[11:15]};
}
endgroup
initial begin
cg_default cg1;
cg_valid cg2;
real cov;
cg1 = new;
cg2 = new;
// Test 1: default bins
$display("Test 1: default bins");
data = 0; cg1.sample(); // special bin
data = 1; cg1.sample(); // default/others bin
data = 5; cg1.sample(); // special bin
data = 7; cg1.sample(); // default/others bin
data = 10; cg1.sample(); // special bin
cov = cg1.get_inst_coverage();
$display(" Coverage with default bins: %0.1f%%", cov);
// Both bins hit: special (3 values: 0,5,10) and default (2 values: 1,7)
// Expected: 2/2 = 100%
if (cov < 99.0 || cov > 101.0) begin
$display("%%Error: Expected 100%%, got %0.1f%%", cov);
error_count++;
end
// Test 2: illegal_bins (test with valid values only)
$display("Test 2: illegal_bins (sampling valid values)");
data = 0; cg2.sample(); // valid
data = 5; cg2.sample(); // valid
data = 10; cg2.sample(); // valid
cov = cg2.get_inst_coverage();
$display(" Coverage with illegal_bins: %0.1f%%", cov);
// Only the valid bin counts, illegal bins don't count toward coverage
// 1 bin out of 1 = 100%
if (cov < 99.0 || cov > 101.0) begin
$display("%%Error: Expected 100%%, got %0.1f%%", cov);
error_count++;
end
if (error_count == 0) begin
$write("*-* All Finished *-*\n");
end else begin
$display("%%Error: %0d test(s) failed", error_count);
$stop;
end
$finish;
end
endmodule

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
# This test documents a known Verilator timing limitation:
# Internal clocks (generated via `always #5 clk = ~clk`) don't properly
# trigger procedural blocks in --timing mode. Even explicit .sample() calls
# in always @(posedge clk) blocks don't execute.
#
# Root cause: Timing scheduler doesn't trigger NBA/active regions for
# internally generated clock edges.
#
# Workaround: Use module input clocks (see t_covergroup_auto_sample.v)
test.compile(verilator_flags2=["--timing"])
test.execute(fails=True, expect=r'%Error: .*Timeout')
test.passes()

View File

@ -0,0 +1,76 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// Test: Covergroup with INTERNAL clock using explicit sampling
// This demonstrates the workaround for internally generated clocks.
//
// Note: Auto-sampling with clocking events (@(posedge clk)) does NOT work
// for internal clocks due to Verilator timing scheduler limitations.
// The sample() call is generated but the NBA region isn't triggered.
//
// Solution: Call .sample() explicitly in an always block.
module t;
logic clk = 0;
always #5 clk = ~clk;
logic [1:0] data;
/* verilator lint_off UNSIGNED */
covergroup cg; // NOTE: No clocking event - we'll sample explicitly
cp: coverpoint data {
bins val0 = {2'b00};
bins val1 = {2'b01};
bins val2 = {2'b10};
bins val3 = {2'b11};
}
endgroup
/* verilator lint_on UNSIGNED */
cg cg_inst = new;
// Explicit sampling workaround for internal clocks
always @(posedge clk) begin
cg_inst.sample();
end
initial begin
// Cycle 0
data = 2'b00;
@(posedge clk);
// Cycle 1
data = 2'b01;
@(posedge clk);
// Cycle 2
data = 2'b10;
@(posedge clk);
// Cycle 3
data = 2'b11;
@(posedge clk);
// Check coverage
#1; // Small delay to ensure last sample completes
begin
automatic real cov = cg_inst.get_inst_coverage();
$display("Coverage: %0.1f%%", cov);
// Should have hit all 4 bins = 100%
if (cov >= 99.0) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Expected 100%% coverage, got %f%%", cov);
$display("ERROR: This is a known limitation - auto-sampling doesn't work with internal clocks");
$stop;
end
end
end
endmodule

View File

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

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// Test: Covergroup with clocking event using MODULE INPUT clock
// Status: WORKS - Verilator correctly auto-samples when clk is a module port
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [1:0] data;
/* verilator lint_off UNSIGNED */
covergroup cg @(posedge clk);
cp: coverpoint data {
bins val0 = {2'b00};
bins val1 = {2'b01};
bins val2 = {2'b10};
bins val3 = {2'b11};
}
endgroup
/* verilator lint_on UNSIGNED */
cg cg_inst = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
// Change data each cycle
data <= cyc[1:0];
if (cyc == 5) begin
/* verilator lint_off IMPLICITSTATIC */
real cov = cg_inst.get_inst_coverage();
/* verilator lint_on IMPLICITSTATIC */
$display("Coverage: %0.1f%%", cov);
// Should have hit all 4 bins (cycles 0-3) = 100%
if (cov >= 99.0) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Expected 100%% coverage, got %f%%", cov);
$stop;
end
end
if (cyc > 10) begin
$display("ERROR: Test timeout");
$stop;
end
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile(verilator_flags2=['--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,82 @@
// 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-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [1:0] data;
// Covergroup with 4 bins
covergroup cg @(posedge clk);
cp: coverpoint data {
bins low = {2'b00};
bins mid1 = {2'b01};
bins mid2 = {2'b10};
bins high = {2'b11};
}
endgroup
cg cg_inst = new;
initial begin
// Initially no bins covered - should be 0%
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage after 0 samples: %f", cov);
if (cov != 0.0) $stop;
// Cover 1 bin (low) - should be 25%
@(posedge clk);
data = 2'b00;
@(posedge clk);
cov = cg_inst.get_inst_coverage();
$display("Coverage after 1/4 bins: %f", cov);
if (cov < 24.9 || cov > 25.1) begin
$display("%%Error: Expected 25%%, got %f", cov);
$stop;
end
// Cover 2nd bin (mid1) - should be 50%
@(posedge clk);
data = 2'b01;
@(posedge clk);
cov = cg_inst.get_inst_coverage();
$display("Coverage after 2/4 bins: %f", cov);
if (cov < 49.9 || cov > 50.1) begin
$display("%%Error: Expected 50%%, got %f", cov);
$stop;
end
// Cover 3rd bin (mid2) - should be 75%
@(posedge clk);
data = 2'b10;
@(posedge clk);
cov = cg_inst.get_inst_coverage();
$display("Coverage after 3/4 bins: %f", cov);
if (cov < 74.9 || cov > 75.1) begin
$display("%%Error: Expected 75%%, got %f", cov);
$stop;
end
// Cover 4th bin (high) - should be 100%
@(posedge clk);
data = 2'b11;
@(posedge clk);
cov = cg_inst.get_inst_coverage();
$display("Coverage after 4/4 bins: %f", cov);
if (cov < 99.9 || cov > 100.1) begin
$display("%%Error: Expected 100%%, got %f", cov);
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

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

View File

@ -0,0 +1,63 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test querying coverage values via get_inst_coverage
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [3:0] data;
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:15]};
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
// Initially no coverage
check_coverage(0.0, "initial");
// Sample low bin - should be 33.33% (1 of 3 bins)
data = 1;
cg_inst.sample();
check_coverage(33.33, "after low");
// Sample mid bin - should be 66.67% (2 of 3 bins)
data = 5;
cg_inst.sample();
check_coverage(66.67, "after mid");
// Sample high bin - should be 100% (3 of 3 bins)
data = 10;
cg_inst.sample();
check_coverage(100.0, "after high");
// Sample again - coverage should still be 100%
data = 2;
cg_inst.sample();
check_coverage(100.0, "after resample");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(real expected, string label);
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
// Allow 0.5% tolerance for floating point
if (cov < expected - 0.5 || cov > expected + 0.5) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

@ -1,20 +1,3 @@
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:19: Ignoring unsupported: coverage clocking event
21 | 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:22:9: Ignoring unsupported: coverpoint
22 | coverpoint a;
| ^~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:24:31: Ignoring unsupported: cover bin specification
24 | bins the_bins [5] = { [0:20] };
| ^
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:23:9: Ignoring unsupported: coverpoint
23 | coverpoint b {
| ^~~~~~~~~~
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:5: Ignoring unsupported: covergroup
21 | covergroup cg @(posedge clk);
| ^~~~~~~~~~
%Error: t/t_covergroup_coverpoints_unsup.v:35:48: Member 'a' not found in covergroup 'cg'
: ... note: In instance 't'
35 | $display("coverage a = %f", the_cg.a.get_inst_coverage());

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,73 @@
// 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 3-way cross coverage
module t;
logic [1:0] addr;
logic cmd;
logic mode;
// Covergroup with 3-way cross coverage
covergroup cg;
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};
}
// 3-way cross: addr x cmd x mode = 3 x 2 x 2 = 12 cross bins
addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode;
endgroup
cg cg_inst = new;
initial begin
// Hit different 3-way cross bins
addr = 0; cmd = 0; mode = 0; cg_inst.sample(); // addr0 x read x normal
$display("Sample 1: addr=%0d, cmd=%0d, mode=%0d", addr, cmd, mode);
addr = 1; cmd = 1; mode = 0; cg_inst.sample(); // addr1 x write x normal
$display("Sample 2: addr=%0d, cmd=%0d, mode=%0d", addr, cmd, mode);
addr = 2; cmd = 0; mode = 1; cg_inst.sample(); // addr2 x read x debug
$display("Sample 3: addr=%0d, cmd=%0d, mode=%0d", addr, cmd, mode);
addr = 0; cmd = 1; mode = 1; cg_inst.sample(); // addr0 x write x debug
$display("Sample 4: addr=%0d, cmd=%0d, mode=%0d", addr, cmd, mode);
addr = 1; cmd = 0; mode = 1; cg_inst.sample(); // addr1 x read x debug
$display("Sample 5: addr=%0d, cmd=%0d, mode=%0d", addr, cmd, mode);
// Check coverage
// Total bins:
// - 3 bins in cp_addr (addr0, addr1, addr2)
// - 2 bins in cp_cmd (read, write)
// - 2 bins in cp_mode (normal, debug)
// - 12 bins in 3-way cross (3 x 2 x 2)
// Total = 19 bins
// Hit: addr0, addr1, addr2 (3), read, write (2), normal, debug (2), 5 cross bins
// Total = 12 out of 19 = 63.2%
$display("Coverage: %0.1f%%", cg_inst.get_inst_coverage());
if (cg_inst.get_inst_coverage() < 62.0 || cg_inst.get_inst_coverage() > 64.0) begin
$display("%%Error: Expected coverage around 63%%, got %0.1f%%",
cg_inst.get_inst_coverage());
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,74 @@
// 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 4-way cross coverage
module t;
logic [1:0] addr;
logic cmd;
logic mode;
logic parity;
// Covergroup with 4-way cross coverage
covergroup cg;
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};
}
// 4-way cross: addr x cmd x mode x parity = 2 x 2 x 2 x 2 = 16 cross bins
addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity;
endgroup
cg cg_inst = new;
initial begin
// Hit different 4-way cross bins
addr = 0; cmd = 0; mode = 0; parity = 0; cg_inst.sample();
$display("Sample 1: addr=%0d, cmd=%0d, mode=%0d, parity=%0d", addr, cmd, mode, parity);
addr = 1; cmd = 1; mode = 0; parity = 1; cg_inst.sample();
$display("Sample 2: addr=%0d, cmd=%0d, mode=%0d, parity=%0d", addr, cmd, mode, parity);
addr = 0; cmd = 1; mode = 1; parity = 0; cg_inst.sample();
$display("Sample 3: addr=%0d, cmd=%0d, mode=%0d, parity=%0d", addr, cmd, mode, parity);
addr = 1; cmd = 0; mode = 1; parity = 1; cg_inst.sample();
$display("Sample 4: addr=%0d, cmd=%0d, mode=%0d, parity=%0d", addr, cmd, mode, parity);
// Check coverage
// Total bins:
// - 2 bins in cp_addr
// - 2 bins in cp_cmd
// - 2 bins in cp_mode
// - 2 bins in cp_parity
// - 16 bins in 4-way cross (2 x 2 x 2 x 2)
// Total = 24 bins
// Hit: 2+2+2+2+4 = 12 out of 24 = 50%
$display("Coverage: %0.1f%%", cg_inst.get_inst_coverage());
if (cg_inst.get_inst_coverage() < 49.0 || cg_inst.get_inst_coverage() > 51.0) begin
$display("%%Error: Expected coverage around 50%%, got %0.1f%%",
cg_inst.get_inst_coverage());
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,83 @@
// 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 basic cross coverage functionality
module t;
/* verilator lint_off UNSIGNED */
bit [7:0] addr;
bit [7:0] cmd;
covergroup cg;
// Two coverpoints with 2 bins each
a: coverpoint addr {
bins low = {[0:3]};
bins high = {[4:7]};
}
b: coverpoint cmd {
bins read = {0};
bins write = {1};
}
// 2-way cross creates 4 bins: lowread, lowwrite, highread, highwrite
c: cross a, b;
endgroup
initial begin
cg cg_inst;
real cov;
cg_inst = new();
// Initially coverage should be 0%
cov = cg_inst.get_inst_coverage();
if (cov != 0.0) begin
$error("Initial coverage should be 0%%, got %0.2f%%", cov);
end
// Hit lowread
addr = 2; cmd = 0;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
// Should have: a.low(1), b.read(1), c.low_x__read(1) = 3/8 = 37.5%
if (cov < 35.0 || cov > 40.0) begin
$error("After 1 sample, expected ~37.5%%, got %0.2f%%", cov);
end
// Hit highwrite
addr = 5; cmd = 1;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
// Should have: a.low(1), a.high(1), b.read(1), b.write(1),
// c.low_x__read(1), c.high_x__write(1) = 6/8 = 75%
if (cov < 70.0 || cov > 80.0) begin
$error("After 2 samples, expected ~75%%, got %0.2f%%", cov);
end
// Hit lowwrite
addr = 1; cmd = 1;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
// Should have 7/8 = 87.5%
if (cov < 85.0 || cov > 90.0) begin
$error("After 3 samples, expected ~87.5%%, got %0.2f%%", cov);
end
// Hit highread for 100% coverage
addr = 7; cmd = 0;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
// Should have 8/8 = 100%
if (cov != 100.0) begin
$error("After all bins hit, expected 100%%, got %0.2f%%", cov);
end
$display("Cross coverage test PASSED - final coverage: %0.2f%%", cov);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile(make_main=False,
verilator_flags2=["--coverage-user", "--exe", "t/t_covergroup_cross_large_main.cpp"])
test.execute(check_finished=True)
test.passes()

View File

@ -0,0 +1,86 @@
// 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-License-Identifier: CC0-1.0
// Test large cross coverage with sparse map implementation
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc = 0;
logic [3:0] a;
logic [3:0] b;
logic [3:0] c;
logic [3:0] d;
covergroup cg @(posedge clk);
option.per_instance = 1;
// Each coverpoint has 4 bins, total cross: 4444 = 256 bins
// This exceeds threshold of 64, so should use sparse map
cp_a: coverpoint a {
bins a0 = {0,1,2,3};
bins a1 = {4,5,6,7};
bins a2 = {8,9,10,11};
bins a3 = {12,13,14,15};
}
cp_b: coverpoint b {
bins b0 = {0,1,2,3};
bins b1 = {4,5,6,7};
bins b2 = {8,9,10,11};
bins b3 = {12,13,14,15};
}
cp_c: coverpoint c {
bins c0 = {0,1,2,3};
bins c1 = {4,5,6,7};
bins c2 = {8,9,10,11};
bins c3 = {12,13,14,15};
}
cp_d: coverpoint d {
bins d0 = {0,1,2,3};
bins d1 = {4,5,6,7};
bins d2 = {8,9,10,11};
bins d3 = {12,13,14,15};
}
// 4-way cross: 4444 = 256 bins (> 64 threshold)
cross_abcd: cross cp_a, cp_b, cp_c, cp_d;
endgroup
cg cg_inst = new;
always @(posedge clk) begin
cyc <= cyc + 1;
// Generate some cross coverage
a <= cyc[3:0];
b <= cyc[7:4];
c <= cyc[3:0]; // Intentionally correlate some
d <= cyc[7:4];
if (cyc == 20) begin
/* verilator lint_off IMPLICITSTATIC */
real inst_cov = cg_inst.get_inst_coverage();
/* verilator lint_on IMPLICITSTATIC */
$display("Coverage: %0.1f%%", inst_cov);
if (inst_cov < 1.0 || inst_cov > 100.0) begin
$display("%%Error: Invalid coverage value");
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

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

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,65 @@
// 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 basic cross coverage with 2-way cross
module t;
logic [1:0] addr;
logic cmd;
logic clk;
// Covergroup with cross coverage
covergroup cg;
cp_addr: coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
bins addr2 = {2};
bins addr3 = {3};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
// Cross coverage: addr x cmd = 4 x 2 = 8 bins
addr_cmd: cross cp_addr, cp_cmd;
endgroup
cg cg_inst = new;
initial begin
// Hit different cross bins
addr = 0; cmd = 0; cg_inst.sample(); // addr0 x read
$display("After sample 1: addr=%0d, cmd=%0d", addr, cmd);
addr = 1; cmd = 1; cg_inst.sample(); // addr1 x write
$display("After sample 2: addr=%0d, cmd=%0d", addr, cmd);
addr = 2; cmd = 0; cg_inst.sample(); // addr2 x read
$display("After sample 3: addr=%0d, cmd=%0d", addr, cmd);
addr = 0; cmd = 1; cg_inst.sample(); // addr0 x write
$display("After sample 4: addr=%0d, cmd=%0d", addr, cmd);
// Check coverage - should be 50% (4 out of 8 bins hit)
// Actually, with cross bins, we have:
// - 4 bins in cp_addr: addr0, addr1, addr2, addr3
// - 2 bins in cp_cmd: read, write
// - 8 bins in cross (4 x 2)
// Hit: addr0, addr1, addr2 (3 bins), read, write (2 bins), 4 cross bins
// Total = 9 out of 14 = 64.3%
$display("Coverage: %0.1f%%", cg_inst.get_inst_coverage());
if (cg_inst.get_inst_coverage() < 63.0 || cg_inst.get_inst_coverage() > 65.0) begin
$display("%%Error: Expected coverage around 64%%, got %0.1f%%",
cg_inst.get_inst_coverage());
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,60 @@
// 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-License-Identifier: CC0-1.0
// Test small cross coverage with inline implementation
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc = 0;
logic [3:0] a;
logic [3:0] b;
covergroup cg @(posedge clk);
option.per_instance = 1;
// 2-way cross: 44 = 16 bins (< 64 threshold, should use inline)
cp_a: coverpoint a {
bins a0 = {0,1,2,3};
bins a1 = {4,5,6,7};
bins a2 = {8,9,10,11};
bins a3 = {12,13,14,15};
}
cp_b: coverpoint b {
bins b0 = {0,1,2,3};
bins b1 = {4,5,6,7};
bins b2 = {8,9,10,11};
bins b3 = {12,13,14,15};
}
cross_ab: cross cp_a, cp_b;
endgroup
cg cg_inst = new;
always @(posedge clk) begin
cyc <= cyc + 1;
a <= cyc[3:0];
b <= cyc[7:4];
if (cyc == 20) begin
/* verilator lint_off IMPLICITSTATIC */
real inst_cov = cg_inst.get_inst_coverage();
/* verilator lint_on IMPLICITSTATIC */
$display("Coverage: %0.1f%%", inst_cov);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--coverage'])
test.execute()
# Check that coverage database contains functional coverage entries
# Format uses control characters as delimiters: C '^At^Bcovergroup^Apage...bin^Blow...h^Bcg.cp.low' count
test.file_grep(test.coverage_filename, r'covergroup')
test.file_grep(test.coverage_filename, r'bin.{0,2}low') # binlow with possible delimiter
test.file_grep(test.coverage_filename, r'bin.{0,2}high') # binhigh with possible delimiter
test.file_grep(test.coverage_filename, r'cg\.cp\.low')
test.file_grep(test.coverage_filename, r'cg\.cp\.high')
# Verify both bins have non-zero counts (they were both sampled)
test.file_grep(test.coverage_filename, r'.*bin.{0,2}low.*\' [1-9]')
test.file_grep(test.coverage_filename, r'.*bin.{0,2}high.*\' [1-9]')
test.passes()

View File

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

View File

@ -0,0 +1,66 @@
// 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
module t;
/* verilator lint_off UNSIGNED */
bit [7:0] data;
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins high = {[12:15]};
bins other = default; // Catches everything else (4-11, 16+)
}
endgroup
initial begin
cg cg_inst;
real cov;
cg_inst = new();
// Hit low bin
data = 2;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After low: %0.2f%%", cov);
if (cov < 30.0 || cov > 35.0) begin
$error("Expected ~33.33%% (1/3 bins), got %0.2f%%", cov);
end
// Hit high bin
data = 14;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After high: %0.2f%%", cov);
if (cov < 63.0 || cov > 70.0) begin
$error("Expected ~66.67%% (2/3 bins), got %0.2f%%", cov);
end
// Hit default bin with value 7 (not in low or high)
data = 7;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display("After default (7): %0.2f%%", cov);
if (cov != 100.0) begin
$error("Expected 100%% (3/3 bins), got %0.2f%%", cov);
end
// Hit another default value (should not increase coverage)
data = 20;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
if (cov != 100.0) begin
$error("Coverage should stay 100%%, got %0.2f%%", cov);
end
$display("Default bins test PASSED");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,93 @@
// 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-License-Identifier: CC0-1.0
// Test dynamic covergroup creation with 'new' operator
module t;
covergroup cg;
coverpoint data {
bins low = {[0:1]};
bins high = {[2:3]};
}
endgroup
int data;
initial begin
cg cg_inst;
real cov;
// Test 1: Create single dynamic instance
$display("Test 1: Single dynamic instance");
cg_inst = new;
// Initially no coverage
cov = cg_inst.get_inst_coverage();
$display(" Initial coverage: %f", cov);
if (cov != 0.0) $stop;
// Sample low bin
data = 0;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display(" After sampling low: %f", cov);
if (cov < 49.0 || cov > 51.0) $stop; // ~50%
// Sample high bin
data = 2;
cg_inst.sample();
cov = cg_inst.get_inst_coverage();
$display(" After sampling high: %f", cov);
if (cov < 99.0 || cov > 101.0) $stop; // ~100%
// Test 2: Multiple dynamic instances
$display("Test 2: Multiple dynamic instances");
begin
cg cg1, cg2, cg3;
cg1 = new;
cg2 = new;
cg3 = new;
// Sample different bins in each
data = 0;
cg1.sample();
data = 2;
cg2.sample();
data = 1;
cg3.sample();
// Check individual coverage
cov = cg1.get_inst_coverage();
$display(" cg1 coverage: %f", cov);
if (cov < 49.0 || cov > 51.0) $stop; // 50%
cov = cg2.get_inst_coverage();
$display(" cg2 coverage: %f", cov);
if (cov < 49.0 || cov > 51.0) $stop; // 50%
cov = cg3.get_inst_coverage();
$display(" cg3 coverage: %f", cov);
if (cov < 49.0 || cov > 51.0) $stop; // 50%
end
// Test 3: Reassignment (old instance should be cleaned up)
$display("Test 3: Instance reassignment");
cg_inst = new; // Create new, old should be freed
// New instance starts with 0% coverage
cov = cg_inst.get_inst_coverage();
$display(" New instance coverage: %f", cov);
if (cov != 0.0) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,28 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
// Simple test harness for t_covergroup_empty - provides clock
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
#include "verilated.h"
#include "Vt_covergroup_empty.h"
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vt_covergroup_empty* top = new Vt_covergroup_empty;
// Run for 20 cycles
for (int i = 0; i < 20; i++) {
top->clk = 0;
top->eval();
top->clk = 1;
top->eval();
if (Verilated::gotFinish()) break;
}
delete top;
return 0;
}

View File

@ -0,0 +1,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()
test.passes()

View File

@ -0,0 +1,54 @@
// DESCRIPTION: Verilator: Verilog Test module - Edge case: empty covergroup
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// 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
// Get coverage - should be 100% (nothing to fail)
begin
real cov;
cov = cg_inst.get_inst_coverage();
$display("Empty covergroup coverage: %f%%", cov);
// Empty covergroup should report 100% coverage
if (cov >= 99.9) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Expected 100%% coverage for empty covergroup, got %f%%", cov);
$stop;
end
end
end
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

@ -11,6 +11,11 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
# Covergroup inheritance with 'extends' is not yet supported
test.compile(
fails=test.vlt_all,
expect=
r'%Error: t/t_covergroup_extends.v:\d+:\d+: Unsupported: covergroup inheritance \(extends\) is not implemented'
)
test.passes()

View File

@ -11,6 +11,11 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
# Covergroup inheritance with 'extends' is not yet supported
test.compile(
fails=test.vlt_all,
expect=
r'%Error: t/t_covergroup_extends_newfirst.v:\d+:\d+: Unsupported: covergroup inheritance \(extends\) is not implemented'
)
test.passes()

View File

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

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (input clk);
int value = 0;
covergroup cg;
cp: coverpoint value {
bins low = {[0:5]};
}
endgroup
cg my_cg = new;
always @(posedge clk) begin
real cov;
cov = my_cg.get_inst_coverage();
my_cg.sample();
end
endmodule

View File

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

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (input clk);
logic enable = 0;
int value = 0;
covergroup cg_iff;
cp_value: coverpoint value iff (enable) {
bins low = {[0:5]};
bins mid = {[6:10]};
}
endgroup
cg_iff cg = new;
always @(posedge clk) begin
cg.sample();
end
endmodule

View File

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

View File

@ -0,0 +1,63 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test ignore_bins - excluded from coverage
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [3:0] data;
covergroup cg;
coverpoint data {
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:11]};
ignore_bins reserved = {[12:15]}; // Should not count toward coverage
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
// Initially 0% (0 of 3 regular bins)
check_coverage(0.0, "initial");
// Hit reserved bin - should still be 0%
data = 13;
cg_inst.sample();
check_coverage(0.0, "after reserved");
// Hit low bin - now 33.33% (1 of 3)
data = 1;
cg_inst.sample();
check_coverage(33.33, "after low");
// Hit another reserved value - still 33.33%
data = 15;
cg_inst.sample();
check_coverage(33.33, "after another reserved");
// Complete regular bins
data = 5; cg_inst.sample(); // mid
data = 10; cg_inst.sample(); // high
check_coverage(100.0, "complete");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(real expected, string label);
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
if (cov < expected - 0.5 || cov > expected + 0.5) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Test that illegal_bins are excluded from coverage (like ignore_bins)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,39 @@
// 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-License-Identifier: CC0-1.0
module t;
/* verilator lint_off UNSIGNED */
logic [1:0] data;
covergroup cg;
coverpoint data {
bins low = {0};
bins mid = {1};
bins high = {2};
illegal_bins forbidden = {3};
}
endgroup
initial begin
automatic cg cg_inst = new;
// Sample legal values only
data = 0;
cg_inst.sample();
$display("Coverage after low: %f%% (expected ~33.33%%)", cg_inst.get_inst_coverage());
data = 1;
cg_inst.sample();
$display("Coverage after mid: %f%% (expected ~66.67%%)", cg_inst.get_inst_coverage());
data = 2;
cg_inst.sample();
$display("Coverage complete: %f%% (expected ~100.00%%)", cg_inst.get_inst_coverage());
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

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

View File

@ -0,0 +1,2 @@
Coverage: 0.0%
*-* All Finished *-*

View File

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

View File

@ -0,0 +1,34 @@
// 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
// Minimal test for covergroup parsing and code generation
module t;
int unsigned addr; // Use unsigned to avoid comparison warnings
covergroup cg;
cp_addr: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
endgroup
initial begin
cg cg_inst;
cg_inst = new;
// Sample some values
addr = 10;
cg_inst.sample();
addr = 200;
cg_inst.sample();
$display("Coverage: %0.1f%%", cg_inst.get_coverage());
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

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

View File

@ -0,0 +1,59 @@
// 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 mixed bin types: single values and ranges
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [7:0] opcode;
covergroup cg;
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 others = {[8'h20:8'hFE]}; // Avoid 0xFF to prevent CMPCONST warning
}
endgroup
cg cg_inst;
initial begin
cg_inst = new;
// Test single value bins
opcode = 8'h00; cg_inst.sample(); // nop
check_coverage(20.0, "after nop");
// Test multi-value list bin
opcode = 8'h02; cg_inst.sample(); // load
check_coverage(40.0, "after load");
opcode = 8'h05; cg_inst.sample(); // store
check_coverage(60.0, "after store");
// Test range bin
opcode = 8'h15; cg_inst.sample(); // arith
check_coverage(80.0, "after arith");
opcode = 8'h80; cg_inst.sample(); // others
check_coverage(100.0, "after others");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(real expected, string label);
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
if (cov < expected - 0.5 || cov > expected + 0.5) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

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

View File

@ -0,0 +1,60 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test multiple covergroup instances with separate tracking
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [3:0] data1, data2;
covergroup cg;
coverpoint data1 {
bins low = {[0:3]};
bins high = {[4:15]};
}
endgroup
cg cg_inst1, cg_inst2;
initial begin
cg_inst1 = new;
cg_inst2 = new;
// Initially both have 0% coverage
check_coverage(cg_inst1, 0.0, "inst1 initial");
check_coverage(cg_inst2, 0.0, "inst2 initial");
// Sample different values in each instance
data1 = 1;
cg_inst1.sample(); // inst1: low covered (50%)
check_coverage(cg_inst1, 50.0, "inst1 after low");
check_coverage(cg_inst2, 0.0, "inst2 still empty");
data1 = 10;
cg_inst2.sample(); // inst2: high covered (50%)
check_coverage(cg_inst1, 50.0, "inst1 still 50%");
check_coverage(cg_inst2, 50.0, "inst2 after high");
// Complete coverage in inst1
data1 = 8;
cg_inst1.sample(); // inst1: both covered (100%)
check_coverage(cg_inst1, 100.0, "inst1 complete");
check_coverage(cg_inst2, 50.0, "inst2 still 50%");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(cg inst, real expected, string label);
real cov;
cov = inst.get_inst_coverage();
$display("Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
if (cov < expected - 0.5 || cov > expected + 0.5) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,79 @@
// DESCRIPTION: Verilator: Verilog Test module - Edge case: multiple instances
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// Test: Multiple instances of same covergroup type sampling the same coverpoint
// Expected: Each instance tracks coverage independently, achieving same coverage
// since they all sample the same expression (value1)
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
logic [2:0] value1;
covergroup cg;
cp: coverpoint value1 {
bins low = {[0:3]};
bins high = {[4:7]};
}
endgroup
// Create three independent instances
cg cg_inst1 = new;
cg cg_inst2 = new;
cg cg_inst3 = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
0: begin
value1 <= 1; // low bin for all instances
end
1: begin
value1 <= 6; // high bin for all instances -> 100%
end
2: begin
begin
real cov1, cov2, cov3;
cov1 = cg_inst1.get_inst_coverage();
cov2 = cg_inst2.get_inst_coverage();
cov3 = cg_inst3.get_inst_coverage();
$display("Instance 1 coverage: %f%%", cov1);
$display("Instance 2 coverage: %f%%", cov2);
$display("Instance 3 coverage: %f%%", cov3);
// All instances sample the same coverpoint (value1), so they should all be 100%
// This tests that multiple instances track coverage independently,
// even when sampling the same expression
if (cov1 >= 99.0 && cov2 >= 99.0 && cov3 >= 99.0) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Coverage mismatch");
$display(" Expected: inst1=100%%, inst2=100%%, inst3=100%%");
$display(" Got: inst1=%f%%, inst2=%f%%, inst3=%f%%", cov1, cov2, cov3);
$stop;
end
end
end
endcase
// Each instance samples the same value (value1)
// But tracks coverage independently
cg_inst1.sample();
cg_inst2.sample();
cg_inst3.sample();
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,65 @@
// DESCRIPTION: Verilator: Verilog Test module - Edge case: negative value ranges
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// Test: Bins with negative value ranges
// Expected: Should handle negative numbers correctly
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
int signed value;
/* verilator lint_off CMPCONST */
covergroup cg;
cp_neg: coverpoint value {
bins negative = {[-100:-1]};
bins zero = {0};
bins positive = {[1:100]};
bins mixed = {[-10:10]};
}
endgroup
/* verilator lint_on CMPCONST */
cg cg_inst = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
0: value <= -50; // Hit negative bin
1: value <= 0; // Hit zero bin
2: value <= 50; // Hit positive bin
3: value <= -5; // Hit mixed bin (also negative)
4: value <= 5; // Hit mixed bin (also positive)
5: begin
begin
real cov;
cov = cg_inst.get_inst_coverage();
$display("Coverage with negative ranges: %f%%", cov);
// All 4 bins should be hit = 100%
if (cov >= 99.0) begin
$write("*-* All Finished *-*\n");
$finish;
end else begin
$display("ERROR: Expected 100%% coverage, got %f%%", cov);
$stop;
end
end
end
endcase
cg_inst.sample();
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

@ -0,0 +1,6 @@
%Warning-COVERIGN: t/t_covergroup_option_unsup.v:15:13: Ignoring unsupported coverage option: foobar
15 | option.foobar = 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.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

View File

@ -0,0 +1,21 @@
// 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
// Test: unsupported coverage option name in a coverpoint
module t;
logic [3:0] cp_expr;
covergroup cg;
cp1: coverpoint cp_expr {
option.foobar = 1;
}
endgroup
cg cg_inst = new;
initial $finish;
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. 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('simulator')
test.compile(verilator_flags2=["--coverage-user"])
test.execute()
test.passes()

View File

@ -0,0 +1,113 @@
// 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
// Performance test for functional coverage - measures sample() overhead
module t;
logic [7:0] data;
logic [3:0] state;
logic [15:0] addr;
// Large covergroup with multiple coverpoints and many bins
covergroup cg_perf;
// Coverpoint with many bins
cp_data: coverpoint data {
bins d0 = {0};
bins d1 = {1};
bins d2 = {2};
bins d3 = {3};
bins d4 = {4};
bins d5 = {5};
bins d6 = {6};
bins d7 = {7};
bins d8 = {8};
bins d9 = {9};
bins d10 = {[10:19]};
bins d20 = {[20:29]};
bins d30 = {[30:39]};
bins d40 = {[40:49]};
bins d50 = {[50:59]};
bins rest = {[60:255]};
}
cp_state: coverpoint state {
bins s0 = {0};
bins s1 = {1};
bins s2 = {2};
bins s3 = {3};
bins s4 = {4};
bins s5 = {5};
bins s6 = {6};
bins s7 = {7};
bins s8 = {8};
bins s9 = {9};
bins s10 = {10};
bins s11 = {11};
bins s12 = {12};
bins s13 = {13};
bins s14 = {14};
bins s15 = {15};
}
// verilator lint_off UNSIGNED
// verilator lint_off CMPCONST
cp_addr: coverpoint addr {
bins low = {[16'h0000:16'h03FF]}; // [0:1023]
bins mid = {[16'h0400:16'h07FF]}; // [1024:2047]
bins high = {[16'h0800:16'hFFFF]}; // [2048:65535]
}
// verilator lint_on CMPCONST
// verilator lint_on UNSIGNED
// Cross coverage adds more bins
cross_data_state: cross cp_data, cp_state;
endgroup
cg_perf cg_inst = new;
initial begin
automatic longint start_time, end_time, elapsed;
automatic int iterations = 100000;
automatic real avg_time_ns;
$display("=== Functional Coverage Performance Test ===");
$display("Iterations: %0d", iterations);
// Measure sample() overhead
start_time = $time;
for (int i = 0; i < iterations; i++) begin
// Vary the data to hit different bins
data = i[7:0];
state = i[3:0];
addr = i[15:0];
cg_inst.sample();
end
end_time = $time;
elapsed = end_time - start_time;
avg_time_ns = real'(elapsed) / real'(iterations);
$display("Total time: %0d time units", elapsed);
$display("Average time per sample(): %0.2f time units", avg_time_ns);
$display("Coverage: %0.1f%%", cg_inst.get_inst_coverage());
// Performance target: < 100 cycles per sample()
// Assuming 1 time unit = 1 ns, typical CPU @ 3 GHz = 0.33 ns/cycle
// 100 cycles = 33 ns
if (avg_time_ns < 33.0) begin
$display("PASS: Performance within target (< 100 cycles)");
end else begin
$display("WARNING: Performance may need optimization (> 100 cycles)");
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

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

View File

@ -0,0 +1,70 @@
// 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
// Realistic example: Bus transaction coverage
module t (/*AUTOARG*/);
/* verilator lint_off UNSIGNED */
logic [31:0] addr;
logic [1:0] burst_type;
logic valid;
// Coverage for a memory bus interface
covergroup bus_cg;
// Address coverage with interesting regions
coverpoint addr {
bins zero_page = {[32'h0000_0000:32'h0000_00FF]};
bins boot_rom = {[32'h0000_1000:32'h0000_1FFF]};
bins dram = {[32'h4000_0000:32'h7FFF_FFFF]};
bins peripherals = {[32'h8000_0000:32'h9FFF_FFFF]};
}
// Burst type coverage (only when valid)
coverpoint burst_type iff (valid) {
bins single = {2'b00};
bins incr = {2'b01};
bins wrap = {2'b10};
bins reserved = {2'b11};
}
endgroup
bus_cg cg_inst;
initial begin
cg_inst = new;
// Test various transactions
// Boot sequence - should hit zero_page and boot_rom
valid = 1;
addr = 32'h0000_0010; burst_type = 2'b00; cg_inst.sample();
addr = 32'h0000_1100; burst_type = 2'b01; cg_inst.sample();
// After boot
check_coverage(50.0, "after boot");
// DRAM access with wrap burst
addr = 32'h4000_0000; burst_type = 2'b10; cg_inst.sample();
check_coverage(75.0, "after dram access");
// Peripheral access completes all addr bins
addr = 32'h8000_0100; burst_type = 2'b11; cg_inst.sample();
check_coverage(100.0, "complete");
$write("*-* All Finished *-*\n");
$finish;
end
task check_coverage(real expected, string label);
real cov;
cov = cg_inst.get_inst_coverage();
$display("Bus Coverage %s: %0.2f%% (expected ~%0.2f%%)", label, cov, expected);
if (cov < expected - 1.0 || cov > expected + 1.0) begin
$error("Coverage mismatch: got %0.2f%%, expected ~%0.2f%%", cov, expected);
$stop;
end
endtask
endmodule

View File

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

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