Compare commits
105 Commits
c4654e7bf5
...
4bd96875fe
| Author | SHA1 | Date |
|---|---|---|
|
|
4bd96875fe | |
|
|
cd30c22d1c | |
|
|
81f6cdc32c | |
|
|
23ca23b7b5 | |
|
|
f3c63d017a | |
|
|
ffd2c5c69e | |
|
|
93d7d9c417 | |
|
|
935b2564eb | |
|
|
1117a11d4d | |
|
|
417c422ca2 | |
|
|
a3e51c7b36 | |
|
|
6a8c045d7a | |
|
|
db62c103de | |
|
|
cd1c8341e0 | |
|
|
7a9a999f38 | |
|
|
934d66667e | |
|
|
5990090d82 | |
|
|
b869cdeeea | |
|
|
9649a4cdb5 | |
|
|
ecbccaac6c | |
|
|
ef90608281 | |
|
|
d0d919c66c | |
|
|
97d4920182 | |
|
|
4f270c3e41 | |
|
|
271ddc8044 | |
|
|
be7a947899 | |
|
|
f8dbafc1f3 | |
|
|
4ca3ff5ad3 | |
|
|
10afed99aa | |
|
|
49b4b3aac6 | |
|
|
07e3337f80 | |
|
|
084eee0761 | |
|
|
ee87094c35 | |
|
|
aa912537f3 | |
|
|
77e2c0b4e2 | |
|
|
feb11da024 | |
|
|
e578838c75 | |
|
|
64f6ab5069 | |
|
|
3ae3f52e2d | |
|
|
7b0c443f65 | |
|
|
3088782e28 | |
|
|
ae4fb13879 | |
|
|
fe9d9846d6 | |
|
|
8dd992efdb | |
|
|
4a16d39d72 | |
|
|
3334d91e49 | |
|
|
9601b74dab | |
|
|
e0aa381384 | |
|
|
f07d4601f7 | |
|
|
c96c8cd4cc | |
|
|
957a238605 | |
|
|
731d7d3152 | |
|
|
810666f936 | |
|
|
663b9845a2 | |
|
|
8a4b25d800 | |
|
|
8e2977b969 | |
|
|
bf0f8d6035 | |
|
|
5ad6b62615 | |
|
|
52e7874134 | |
|
|
8709d79092 | |
|
|
0fd0791c7c | |
|
|
e35916e95e | |
|
|
10881c40bc | |
|
|
440d990634 | |
|
|
682556cd05 | |
|
|
ac06e1f8be | |
|
|
6df855cb91 | |
|
|
1e16c8bcdd | |
|
|
5633bb488d | |
|
|
c659fe900a | |
|
|
48e3b7888a | |
|
|
0cfc093dd9 | |
|
|
0f76f47864 | |
|
|
c25bb8ab17 | |
|
|
09b83a9f99 | |
|
|
d35df728d7 | |
|
|
7a307eaed6 | |
|
|
d7109df592 | |
|
|
cdfb990ac5 | |
|
|
880829e321 | |
|
|
7620ad9d7e | |
|
|
d4595a0643 | |
|
|
4868caf1cb | |
|
|
18d9b148e4 | |
|
|
ff269866a6 | |
|
|
bbb7930b6a | |
|
|
b886baaca5 | |
|
|
698f66dcf3 | |
|
|
3de8e398c0 | |
|
|
981268edd1 | |
|
|
d789ae3297 | |
|
|
09360d61fe | |
|
|
e400d10603 | |
|
|
37b2b7db07 | |
|
|
7d941a917a | |
|
|
fa371faff4 | |
|
|
832bcd0b45 | |
|
|
f5605a710b | |
|
|
6de7d603e1 | |
|
|
f9d57688be | |
|
|
3c77283fe3 | |
|
|
8b3e84d352 | |
|
|
b5c5852e1c | |
|
|
5087ee6aa7 | |
|
|
8a6c05870a |
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
ls -lsha
|
||||
tree -L 3 pages
|
||||
- name: Upload pages artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: pages
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ Drew Ranck
|
|||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eric Mejdrich
|
||||
Eric Müller
|
||||
Eric Rippey
|
||||
Eunseo Song
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ union, var, void, priority case/if, and unique case/if.
|
|||
|
||||
It also supports .name and .\* interconnection.
|
||||
|
||||
Verilator partially supports concurrent assert and cover statements; see
|
||||
the enclosed coverage tests for the allowed syntax.
|
||||
Verilator partially supports concurrent assert and cover statements, as well as
|
||||
SystemVerilog functional coverage with ``covergroup``, ``coverpoint``, bins,
|
||||
cross coverage, and transition bins. See :ref:`Functional Coverage<user
|
||||
coverage>` for details.
|
||||
|
||||
Verilator has limited support for class and related object-oriented
|
||||
constructs.
|
||||
|
|
@ -363,10 +365,15 @@ appropriate width.
|
|||
Assertions
|
||||
----------
|
||||
|
||||
Verilator is beginning to add support for assertions. Verilator currently
|
||||
only converts assertions to simple ``if (...) error`` statements, and
|
||||
coverage statements to increment the line counters described in the
|
||||
coverage section.
|
||||
Verilator partially supports assertions and functional coverage.
|
||||
Verilator currently converts assertions to simple ``if (...) error`` statements,
|
||||
and simple coverage statements to increment the line counters described in the
|
||||
:ref:`coverage section<Coverage>`.
|
||||
|
||||
Verilator also partially supports SystemVerilog functional coverage with
|
||||
``covergroup``, ``coverpoint``, bins, cross coverage, and transition bins. See
|
||||
:ref:`Functional Coverage<user coverage>` for details on using
|
||||
covergroups for comprehensive coverage analysis.
|
||||
|
||||
Verilator does not support SEREs yet. All assertion and coverage statements
|
||||
must be simple expressions that complete in one cycle.
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ Verilator supports adding code to the Verilated model to support
|
|||
SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables
|
||||
all forms of coverage:
|
||||
|
||||
- :ref:`User Coverage`
|
||||
- :ref:`Property Coverage`
|
||||
- :ref:`Covergroup Coverage`
|
||||
- :ref:`Line Coverage`
|
||||
- :ref:`Toggle Coverage`
|
||||
|
||||
|
|
@ -192,22 +193,60 @@ When a model with coverage is executed, it will create a coverage file for
|
|||
collection and later analysis, see :ref:`Coverage Collection`.
|
||||
|
||||
|
||||
.. _user coverage:
|
||||
.. _property coverage:
|
||||
|
||||
Functional Coverage
|
||||
-------------------
|
||||
Property Coverage
|
||||
-----------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate functional coverage points the user has inserted manually in
|
||||
SystemVerilog code through into the Verilated model.
|
||||
translate property coverage points the user has inserted manually in
|
||||
SystemVerilog code into the Verilated model.
|
||||
|
||||
For example, the following SystemVerilog statement will add a coverage
|
||||
point under the coverage name "DefaultClock":
|
||||
For simple coverage points, use the ``cover property`` construct:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
DefaultClock: cover property (@(posedge clk) cyc==3);
|
||||
|
||||
This adds a coverage point that tracks whether the condition has been observed.
|
||||
|
||||
.. _covergroup coverage:
|
||||
|
||||
Covergroup Coverage
|
||||
-------------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate covergroup coverage points the user has inserted manually in
|
||||
SystemVerilog code into the Verilated model. Verilator supports
|
||||
coverpoints with value and transition bins, and cross points.
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
module top;
|
||||
logic [7:0] addr;
|
||||
logic cmd;
|
||||
|
||||
// Define a covergroup
|
||||
covergroup cg;
|
||||
cp_addr: coverpoint addr {
|
||||
bins low = {[0:127]};
|
||||
bins high = {[128:255]};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Instantiate the covergroup
|
||||
cg cg_inst = new;
|
||||
|
||||
always @(posedge clk) begin
|
||||
// Sample coverage explicitly
|
||||
cg_inst.sample();
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
||||
.. _line coverage:
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ set(HEADERS
|
|||
V3Active.h
|
||||
V3ActiveTop.h
|
||||
V3Assert.h
|
||||
V3AssertNfa.h
|
||||
V3AssertPre.h
|
||||
V3AssertProp.h
|
||||
V3Ast.h
|
||||
V3AstAttr.h
|
||||
V3AstInlines.h
|
||||
|
|
@ -70,6 +70,7 @@ set(HEADERS
|
|||
V3Control.h
|
||||
V3Coverage.h
|
||||
V3CoverageJoin.h
|
||||
V3Covergroup.h
|
||||
V3Dead.h
|
||||
V3DebugBisect.h
|
||||
V3Delayed.h
|
||||
|
|
@ -201,6 +202,7 @@ set(HEADERS
|
|||
V3WidthCommit.h
|
||||
V3WidthRemove.h
|
||||
VlcBucket.h
|
||||
VlcCovergroup.h
|
||||
VlcOptions.h
|
||||
VlcPoint.h
|
||||
VlcSource.h
|
||||
|
|
@ -213,8 +215,8 @@ set(COMMON_SOURCES
|
|||
V3Active.cpp
|
||||
V3ActiveTop.cpp
|
||||
V3Assert.cpp
|
||||
V3AssertNfa.cpp
|
||||
V3AssertPre.cpp
|
||||
V3AssertProp.cpp
|
||||
V3Ast.cpp
|
||||
V3AstNodes.cpp
|
||||
V3Begin.cpp
|
||||
|
|
@ -236,6 +238,7 @@ set(COMMON_SOURCES
|
|||
V3Const__gen.cpp
|
||||
V3Coverage.cpp
|
||||
V3CoverageJoin.cpp
|
||||
V3Covergroup.cpp
|
||||
V3Dead.cpp
|
||||
V3Delayed.cpp
|
||||
V3Depth.cpp
|
||||
|
|
|
|||
|
|
@ -232,8 +232,8 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Active.o \
|
||||
V3ActiveTop.o \
|
||||
V3Assert.o \
|
||||
V3AssertNfa.o \
|
||||
V3AssertPre.o \
|
||||
V3AssertProp.o \
|
||||
V3Begin.o \
|
||||
V3Branch.o \
|
||||
V3CCtors.o \
|
||||
|
|
@ -249,6 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Coverage.o \
|
||||
V3Covergroup.o \
|
||||
V3CoverageJoin.o \
|
||||
V3Dead.o \
|
||||
V3Delayed.o \
|
||||
|
|
|
|||
139
src/V3Active.cpp
139
src/V3Active.cpp
|
|
@ -619,11 +619,150 @@ public:
|
|||
~ActiveVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Pass 1: collect sample CFuncs and sampling events from covergroup class scopes
|
||||
|
||||
class CovergroupCollectVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// Netlist:
|
||||
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
|
||||
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
|
||||
|
||||
// STATE
|
||||
AstClass* m_classp = nullptr; // Current covergroup class context, or nullptr
|
||||
|
||||
// VISITORS
|
||||
void visit(AstClass* nodep) override {
|
||||
if (!nodep->isCovergroup()) return;
|
||||
VL_RESTORER(m_classp);
|
||||
m_classp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstScope* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void visit(AstCFunc* nodep) override {
|
||||
if (!m_classp) return;
|
||||
if (nodep->isCovergroupSample()) m_classp->user1p(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// V3Covergroup guarantees: only supported-event covergroups survive to V3Active,
|
||||
// and they are always inside a covergroup class (so m_classp is set).
|
||||
// Unlink eventp from cgp so it survives cgp's deletion,
|
||||
// then store it in user2p for use during the second pass.
|
||||
m_classp->user2p(nodep->eventp()->unlinkFrBack());
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CovergroupCollectVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CovergroupCollectVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Pass 2: inject automatic sample() calls for covergroup instances
|
||||
|
||||
class CovergroupInjectVisitor final : public VNVisitor {
|
||||
// NODE STATE (set by CovergroupCollectVisitor, consumed here)
|
||||
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
|
||||
// AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any)
|
||||
|
||||
// STATE
|
||||
ActiveNamer m_namer; // Reuse active naming infrastructure
|
||||
|
||||
// VISITORS
|
||||
void visit(AstScope* nodep) override {
|
||||
m_namer.main(nodep); // Initialize active naming for this scope
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstVarScope* nodep) override {
|
||||
// Get the underlying var
|
||||
AstVar* const varp = nodep->varp();
|
||||
UASSERT_OBJ(varp, nodep, "AstVarScope must have non-null varp");
|
||||
|
||||
// Check if the variable is of covergroup class type
|
||||
const AstNodeDType* const dtypep = varp->dtypep();
|
||||
UASSERT_OBJ(dtypep, nodep, "AstVar must have non-null dtypep after V3Width");
|
||||
|
||||
const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType);
|
||||
if (!classRefp) return;
|
||||
|
||||
AstClass* const classp = classRefp->classp();
|
||||
|
||||
// Check if this covergroup has an automatic sampling event
|
||||
AstSenTree* const eventp = VN_CAST(classp->user2p(), SenTree);
|
||||
if (!eventp) return; // No automatic sampling for this covergroup
|
||||
|
||||
// V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc
|
||||
AstCFunc* const sampleCFuncp = VN_AS(classp->user1p(), CFunc);
|
||||
UASSERT_OBJ(sampleCFuncp, nodep,
|
||||
"No sample() CFunc found for covergroup " << classp->name());
|
||||
|
||||
// Create a VarRef to the covergroup instance for the method call
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstVarRef* const varrefp = new AstVarRef{fl, nodep, VAccess::READ};
|
||||
|
||||
// Create the CMethodCall to sample()
|
||||
// Note: We don't pass arguments in argsp since vlSymsp is passed via argTypes
|
||||
AstCMethodCall* const cmethodCallp
|
||||
= new AstCMethodCall{fl, varrefp, sampleCFuncp, nullptr};
|
||||
|
||||
cmethodCallp->dtypeSetVoid();
|
||||
cmethodCallp->argTypes("vlSymsp");
|
||||
|
||||
// Clone the sensitivity for this active block.
|
||||
// V3Scope has already resolved all VarRefs in eventp, so the clone
|
||||
// inherits correct varScopep values with no fixup needed.
|
||||
AstSenTree* senTreep = eventp->cloneTree(false);
|
||||
|
||||
// Get or create the AstActive node for this sensitivity
|
||||
// senTreep is a template used by getActive() which clones it into the AstActive;
|
||||
// delete it afterwards as it is not added to the AST directly.
|
||||
AstActive* const activep = m_namer.getActive(fl, senTreep);
|
||||
VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
|
||||
|
||||
// Wrap the sample() call in an AstAlways so SchedPartition handles it
|
||||
// via visit(AstNodeProcedure*) like any other clocked always block.
|
||||
activep->addStmtsp(
|
||||
new AstAlways{fl, VAlwaysKwd::ALWAYS_FF, nullptr, cmethodCallp->makeStmt()});
|
||||
}
|
||||
|
||||
void visit(AstClass* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Delete the owned sampling event template stored during collection
|
||||
if (AstSenTree* const eventp = VN_CAST(nodep->user2p(), SenTree)) {
|
||||
VL_DO_DANGLING(pushDeletep(eventp), eventp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstActive*) override {} // Don't iterate into actives
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CovergroupInjectVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CovergroupInjectVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
if (v3Global.useCovergroup()) {
|
||||
// Add automatic covergroup sampling in two focused passes.
|
||||
// user1p/user2p on AstClass span both passes; guards must outlive both visitors.
|
||||
const VNUser1InUse user1InUse;
|
||||
const VNUser2InUse user2InUse;
|
||||
CovergroupCollectVisitor{nodep}; // Pass 1: collect CFuncs and events into user#p
|
||||
CovergroupInjectVisitor{nodep}; // Pass 2: inject sample() calls, delete user2p events
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Implementation of assertion properties
|
||||
// DESCRIPTION: Verilator: NFA-based multi-cycle SVA assertion evaluation
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3ASSERTPROP_H_
|
||||
#define VERILATOR_V3ASSERTPROP_H_
|
||||
#ifndef VERILATOR_V3ASSERTNFA_H_
|
||||
#define VERILATOR_V3ASSERTNFA_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
|
@ -24,9 +24,9 @@ class AstNetlist;
|
|||
|
||||
//============================================================================
|
||||
|
||||
class V3AssertProp final {
|
||||
class V3AssertNfa final {
|
||||
public:
|
||||
static void assertPropAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void assertNfaAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -422,8 +422,10 @@ private:
|
|||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||
const AstConst* const constp = VN_CAST(valuep, Const);
|
||||
if (!constp) {
|
||||
nodep->v3error(
|
||||
"Delay value is not an elaboration-time constant (IEEE 1800-2023 16.7)");
|
||||
// V3AssertNfa handles non-const delays before this pass and
|
||||
// replaces the property; this branch should never be reached.
|
||||
nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
|
||||
"should have been caught by V3AssertNfa");
|
||||
} else if (constp->isZero()) {
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
if (m_inSynchDrive) {
|
||||
|
|
@ -704,9 +706,11 @@ private:
|
|||
}
|
||||
void visit(AstSConsRep* nodep) override {
|
||||
// IEEE 1800-2023 16.9.2 -- Lower standalone exact [*N] (N >= 2) via saturating counter.
|
||||
// Range/unbounded forms and SExpr-contained forms are lowered by V3AssertProp.
|
||||
// Range/unbounded forms and SExpr-contained forms are lowered by V3AssertNfa.
|
||||
iterateChildren(nodep);
|
||||
if (nodep->unbounded() || nodep->maxCountp()) return; // Handled by V3AssertProp
|
||||
// V3AssertNfa handles unbounded/ranged forms upstream, so this fast-path
|
||||
// is effectively unreachable when NFA is enabled.
|
||||
if (nodep->unbounded() || nodep->maxCountp()) return; // LCOV_EXCL_LINE
|
||||
const AstConst* const constp = VN_CAST(nodep->countp(), Const);
|
||||
if (VL_UNLIKELY(!constp || constp->toSInt() < 1)) {
|
||||
nodep->v3fatalSrc("Consecutive repetition count must be a positive constant"
|
||||
|
|
@ -1095,7 +1099,7 @@ private:
|
|||
|
||||
if (AstPExpr* const pexprp = VN_CAST(rhsp, PExpr)) {
|
||||
// Implication with sequence expression on RHS (IEEE 1800-2023 16.11, 16.12.7).
|
||||
// The PExpr was already lowered from the property expression by V3AssertProp.
|
||||
// The PExpr was lowered from the property expression earlier in this pass.
|
||||
// Wrap the PExpr body with the antecedent check so the sequence only
|
||||
// starts when the antecedent holds.
|
||||
AstNodeExpr* condp;
|
||||
|
|
@ -1212,13 +1216,15 @@ private:
|
|||
iterate(nodep->propp());
|
||||
}
|
||||
void visit(AstPExpr* nodep) override {
|
||||
if (m_pexprp && m_pexprp->user1()) {
|
||||
// V3AssertNfa handles multi-cycle property expressions before this pass,
|
||||
// so the following unsupported paths are defensive and typically unreached.
|
||||
if (m_pexprp && m_pexprp->user1()) { // LCOV_EXCL_START
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Complex property expression inside 'until''");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
if (AstLogNot* const notp = VN_CAST(nodep->backp(), LogNot)) {
|
||||
notp->replaceWith(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(notp), notp);
|
||||
|
|
@ -1227,30 +1233,19 @@ private:
|
|||
}
|
||||
// Sequence expression as antecedent of implication is not yet supported
|
||||
if (AstImplication* const implp = VN_CAST(nodep->backp(), Implication)) {
|
||||
if (implp->lhsp() == nodep) {
|
||||
if (implp->lhsp() == nodep) { // LCOV_EXCL_START
|
||||
implp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Implication with sequence expression as antecedent");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
VL_RESTORER(m_pexprp);
|
||||
VL_RESTORER(m_disableSeqIfp);
|
||||
m_pexprp = nodep;
|
||||
|
||||
if (m_disablep) {
|
||||
const AstSampled* sampledp;
|
||||
if (m_disablep->exists([&sampledp](const AstSampled* const sp) {
|
||||
sampledp = sp;
|
||||
return true;
|
||||
})) {
|
||||
sampledp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: $sampled inside disabled condition of a sequence");
|
||||
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
|
||||
// always a copy is used, so remove it now
|
||||
pushDeletep(m_disablep);
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
// Add counter which counts times the condition turned true
|
||||
AstVar* const disableCntp
|
||||
|
|
|
|||
1446
src/V3AssertProp.cpp
1446
src/V3AssertProp.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -1107,6 +1107,85 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
|
|||
|
||||
//######################################################################
|
||||
|
||||
class VCoverBinsType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
BINS_ARRAY, // Array of bins with user-speciifed size
|
||||
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
|
||||
BINS_DEFAULT, // Default bin
|
||||
BINS_IGNORE, // Ignore bin
|
||||
BINS_ILLEGAL, // Illegal bin
|
||||
BINS_TRANSITION, // Transition bin
|
||||
BINS_USER, // Single bin with one or more values/ranges
|
||||
BINS_WILDCARD // Wildcard bin
|
||||
};
|
||||
enum en m_e;
|
||||
VCoverBinsType() // LCOV_EXCL_START
|
||||
: m_e{BINS_USER} {} // LCOV_EXCL_STOP
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCoverBinsType(en _e)
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VCoverOptionType final {
|
||||
public:
|
||||
enum en : uint8_t { WEIGHT, GOAL, AT_LEAST, AUTO_BIN_MAX, PER_INSTANCE, COMMENT, UNKNOWN };
|
||||
enum en m_e;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCoverOptionType(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"weight", "goal", "at_least", "auto_bin_max", "per_instance", "comment", "unknown"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VTransRepType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
NONE, // No repetition
|
||||
CONSEC, // Consecutive repetition [*]
|
||||
GOTO, // Goto repetition [->]
|
||||
NONCONS // Nonconsecutive repetition [=]
|
||||
};
|
||||
enum en m_e;
|
||||
VTransRepType() // LCOV_EXCL_START
|
||||
: m_e{NONE} {} // LCOV_EXCL_STOP
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VTransRepType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VTransRepType(int _e) // LCOV_EXCL_START
|
||||
: m_e(static_cast<en>(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* asciiJson() const {
|
||||
static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VDirection final {
|
||||
public:
|
||||
enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF };
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public:
|
|||
// Someday we will generically support data types on every expr node
|
||||
// Until then isOpaque indicates we shouldn't constant optimize this node type
|
||||
bool isOpaque() const { return VN_IS(this, CvtPackString); }
|
||||
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
|
||||
virtual bool isMultiCycleSva() const { return false; }
|
||||
bool isLValue() const;
|
||||
|
||||
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
|
||||
|
|
@ -2172,6 +2174,7 @@ public:
|
|||
return m_unbounded == VN_DBG_AS(samep, SConsRep)->m_unbounded; // LCOV_EXCL_LINE
|
||||
}
|
||||
bool unbounded() const { return m_unbounded; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSExpr final : public AstNodeExpr {
|
||||
// Sequence expression
|
||||
|
|
@ -2196,6 +2199,7 @@ public:
|
|||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSFormatArg final : public AstNodeExpr {
|
||||
// Information for formatting each argument to AstSFormat,
|
||||
|
|
@ -2314,6 +2318,7 @@ public:
|
|||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSNonConsRep final : public AstNodeExpr {
|
||||
// Nonconsecutive repetition: expr [= count]
|
||||
|
|
@ -2330,6 +2335,7 @@ public:
|
|||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSScanF final : public AstNodeExpr {
|
||||
// @astgen op1 := exprsp : List[AstNodeExpr] // VarRefs for results
|
||||
|
|
@ -3741,6 +3747,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSIntersect final : public AstNodeBiop {
|
||||
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
||||
|
|
@ -3765,6 +3772,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
// LCOV_EXCL_STOP
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSOr final : public AstNodeBiop {
|
||||
// Sequence 'or' (IEEE 1800-2023 16.9.7): at least one operand sequence must match.
|
||||
|
|
@ -3787,6 +3795,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSThroughout final : public AstNodeBiop {
|
||||
// expr throughout seq (IEEE 1800-2023 16.9.9)
|
||||
|
|
@ -3809,6 +3818,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
// LCOV_EXCL_STOP
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSel final : public AstNodeBiop {
|
||||
// *Resolved* (tyep checked) multiple bit range extraction. Always const width
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
|||
bool m_verilogTask : 1; // Declared by user as task (versus internal-made)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_isCovergroupSample : 1; // Covergroup sample() method
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
VLifetime m_lifetime; // Default lifetime of local vars
|
||||
VIsCached m_purity; // Pure state
|
||||
|
|
@ -144,7 +145,8 @@ protected:
|
|||
, m_verilogFunction{false}
|
||||
, m_verilogTask{false}
|
||||
, m_virtual{false}
|
||||
, m_needProcess{false} {
|
||||
, m_needProcess{false}
|
||||
, m_isCovergroupSample{false} {
|
||||
addStmtsp(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
|
@ -218,6 +220,8 @@ public:
|
|||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool isCovergroupSample() const { return m_isCovergroupSample; }
|
||||
void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; }
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
|
|
@ -253,6 +257,19 @@ 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; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstNodeGen VL_NOT_FINAL : public AstNode {
|
||||
// Generate construct
|
||||
public:
|
||||
|
|
@ -513,6 +530,7 @@ class AstCFunc final : public AstNode {
|
|||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
|
||||
// state
|
||||
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
|
||||
int m_cost; // Function call cost
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||
|
|
@ -543,6 +561,7 @@ public:
|
|||
m_dpiImportWrapper = false;
|
||||
m_recursive = false;
|
||||
m_noLife = false;
|
||||
m_isCovergroupSample = false;
|
||||
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
|
|
@ -618,6 +637,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 {
|
||||
|
|
@ -758,13 +779,16 @@ public:
|
|||
class AstCgOptionAssign final : public AstNode {
|
||||
// A covergroup set of option
|
||||
// Parents: CLASS(covergroup) or cross
|
||||
string m_name; // Option name
|
||||
const VCoverOptionType m_optType; // Option type
|
||||
const string m_rawName; // Original option name (for diagnostics on unknown options)
|
||||
const bool m_typeOption; // type_option vs option
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
public:
|
||||
AstCgOptionAssign(FileLine* fl, bool typeOption, const string& name, AstNodeExpr* valuep)
|
||||
AstCgOptionAssign(FileLine* fl, bool typeOption, VCoverOptionType optType,
|
||||
const string& rawName, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CgOptionAssign(fl)
|
||||
, m_name{name}
|
||||
, m_optType{optType}
|
||||
, m_rawName{rawName}
|
||||
, m_typeOption{typeOption} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
|
|
@ -772,7 +796,8 @@ public:
|
|||
// ACCESSORS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name
|
||||
string name() const override VL_MT_STABLE { return m_rawName; }
|
||||
VCoverOptionType optionType() const { return m_optType; }
|
||||
bool typeOption() const { return m_typeOption; }
|
||||
};
|
||||
class AstClassExtends final : public AstNode {
|
||||
|
|
@ -1020,6 +1045,142 @@ public:
|
|||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstCoverBin final : public AstNode {
|
||||
// Captures data for a coverpoint 'bins' declaration
|
||||
// @astgen op1 := rangesp : List[AstNode]
|
||||
// @astgen op2 := iffp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
|
||||
// @astgen op4 := transp : List[AstCoverTransSet]
|
||||
const string m_name; // Base name of the bin
|
||||
const VCoverBinsType m_type; // Bin type (eg AUTO, IGNORE, ILLEGAL)
|
||||
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
|
||||
|
||||
public:
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
|
||||
bool isWildcard = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{isWildcard ? VCoverBinsType::BINS_WILDCARD
|
||||
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
: (isIgnore ? VCoverBinsType::BINS_IGNORE
|
||||
: VCoverBinsType::BINS_USER))} {
|
||||
if (rangesp) addRangesp(rangesp);
|
||||
}
|
||||
// Constructor for automatic bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{VCoverBinsType::BINS_AUTO}
|
||||
, m_isArray{true} {
|
||||
this->arraySizep(arraySizep);
|
||||
}
|
||||
// Constructor for default bins (catch-all)
|
||||
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
// Constructor for transition bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp,
|
||||
VCoverBinsType type = VCoverBinsType::BINS_TRANSITION, bool isArrayBin = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type}
|
||||
, m_isArray{isArrayBin} {
|
||||
UASSERT(transp, "AstCoverBin transition constructor requires non-null transp");
|
||||
addTransp(transp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverBin;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VCoverBinsType binsType() const { return m_type; }
|
||||
bool isArray() const { return m_isArray; }
|
||||
void isArray(bool flag) { m_isArray = flag; }
|
||||
};
|
||||
class AstCoverOption final : public AstNode {
|
||||
// Coverage-option assignment
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
const VCoverOptionType m_type; // Option being assigned
|
||||
|
||||
public:
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_type{type} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverOption;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCoverOptionType optionType() const { return m_type; }
|
||||
};
|
||||
class AstCoverTransItem final : public AstNode {
|
||||
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
|
||||
// @astgen op1 := valuesp : List[AstNode]
|
||||
// @astgen op2 := repMinp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := repMaxp : Optional[AstNodeExpr]
|
||||
const VTransRepType m_repType;
|
||||
|
||||
public:
|
||||
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
|
||||
: ASTGEN_SUPER_CoverTransItem(fl)
|
||||
, m_repType{repType} {
|
||||
if (valuesp) addValuesp(valuesp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverTransItem;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCoverTransSet final : public AstNode {
|
||||
// Represents a transition set: value1 => value2 => value3
|
||||
// @astgen op1 := itemsp : List[AstCoverTransItem]
|
||||
public:
|
||||
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
|
||||
: ASTGEN_SUPER_CoverTransSet(fl) {
|
||||
if (itemsp) addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverTransSet;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCovergroup final : public AstNode {
|
||||
// Represents a covergroup declaration. V3LinkParse transforms this
|
||||
// into an AstClass with isCovergroup==true and attaches the clocking
|
||||
// event as a new AstCovergroup sentinel in class membersp.
|
||||
// @astgen op1 := argsp : List[AstVar]
|
||||
// @astgen op2 := membersp : List[AstNode]
|
||||
// @astgen op3 := eventp : Optional[AstSenTree]
|
||||
// @astgen op4 := sampleArgsp : List[AstVar]
|
||||
string m_name; // covergroup name
|
||||
|
||||
public:
|
||||
AstCovergroup(FileLine* fl, const string& name, AstVar* argsp, AstVar* sampleArgsp,
|
||||
AstNode* membersp, AstSenTree* eventp)
|
||||
: ASTGEN_SUPER_Covergroup(fl)
|
||||
, m_name{name} {
|
||||
if (argsp) addArgsp(argsp);
|
||||
if (sampleArgsp) addSampleArgsp(sampleArgsp);
|
||||
if (membersp) addMembersp(membersp);
|
||||
this->eventp(eventp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCovergroup;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstCoverpointRef final : public AstNode {
|
||||
// Reference to a coverpoint used in a cross
|
||||
const string m_name; // coverpoint name
|
||||
|
||||
public:
|
||||
AstCoverpointRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_CoverpointRef(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstCoverpointRef;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
};
|
||||
class AstDefParam final : public AstNode {
|
||||
// A defparam assignment
|
||||
// Parents: MODULE
|
||||
|
|
@ -2533,6 +2694,39 @@ public:
|
|||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
class AstCoverCross final : public AstNodeFuncCovItem {
|
||||
// @astgen op1 := itemsp : List[AstCoverpointRef]
|
||||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||
// // post-LinkParse: empty
|
||||
public:
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
|
||||
: ASTGEN_SUPER_CoverCross(fl, name) {
|
||||
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCross;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCoverpoint final : public AstNodeFuncCovItem {
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := binsp : List[AstNode] // Parse: mixed AstCoverBin/AstCgOptionAssign;
|
||||
// post-LinkParse: AstCoverBin only
|
||||
// @astgen op3 := iffp : Optional[AstNodeExpr]
|
||||
// @astgen op4 := optionsp : List[AstCoverOption]
|
||||
public:
|
||||
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp,
|
||||
AstNodeExpr* iffp = nullptr, AstNode* binsp = nullptr)
|
||||
: ASTGEN_SUPER_Coverpoint(fl, name) {
|
||||
this->exprp(exprp);
|
||||
this->iffp(iffp);
|
||||
if (binsp) addBinsp(binsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverpoint;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
|
||||
// === AstNodeGen ===
|
||||
class AstGenBlock final : public AstNodeGen {
|
||||
|
|
@ -2615,6 +2809,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)
|
||||
|
|
@ -2642,6 +2838,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);
|
||||
|
|
|
|||
|
|
@ -3536,3 +3536,54 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
|
|||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Functional coverage dump methods
|
||||
|
||||
void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
|
||||
|
||||
void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
|
||||
|
||||
void AstCoverBin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
if (m_isArray) str << "[]";
|
||||
}
|
||||
|
||||
void AstCoverBin::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
|
||||
if (m_isArray) str << ", \"isArray\": true";
|
||||
}
|
||||
|
||||
void AstCoverTransItem::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (m_repType != VTransRepType::NONE) str << " " << m_repType.ascii();
|
||||
}
|
||||
|
||||
void AstCoverTransItem::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"repType\": " << m_repType.asciiJson();
|
||||
}
|
||||
|
||||
void AstCoverTransSet::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
|
||||
void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
|
||||
|
||||
void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
|
||||
|
||||
void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
|
||||
|
||||
void AstCoverOption::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
}
|
||||
|
||||
void AstCoverOption::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"optionType\": \"" << m_type.ascii() << "\"";
|
||||
}
|
||||
|
||||
void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
|
||||
void AstCoverpointRef::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,30 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Covergroup implementation
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3COVERGROUP_H_
|
||||
#define VERILATOR_V3COVERGROUP_H_
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Covergroup final {
|
||||
public:
|
||||
static void covergroup(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -125,6 +125,8 @@ public:
|
|||
// Thanks to the interning, equality is identity
|
||||
bool operator==(const DfgDataType& that) const { return this == &that; }
|
||||
bool operator!=(const DfgDataType& that) const { return this != &that; }
|
||||
// Similarly for hash
|
||||
V3Hash hash() const { return V3Hash{this}; }
|
||||
|
||||
// Type of elements, for arrays only
|
||||
const DfgDataType& elemDtype() const {
|
||||
|
|
@ -132,13 +134,6 @@ public:
|
|||
return *m_elemDtypep;
|
||||
}
|
||||
|
||||
V3Hash hash() const {
|
||||
V3Hash hash{static_cast<uint32_t>(m_kind)};
|
||||
hash += m_size;
|
||||
if (m_elemDtypep) hash += m_elemDtypep->hash();
|
||||
return hash;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Static factory and management functions
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
size_t m_currentGeneration = 0; // Current generation number
|
||||
size_t m_lastId = 0; // Last unique vertex ID assigned
|
||||
size_t m_nTemps = 0; // Number of temporary variables created
|
||||
// Scope for transient temporariy variables cerated in this pass. They should all be
|
||||
// eliminated wihtin this pass, so anything should be ok, pick the top scope as easy to find.
|
||||
AstScope* const m_tmpScopep = v3Global.rootp()->topScopep()->scopep();
|
||||
|
||||
// STATIC STATE
|
||||
static V3DebugBisect s_debugBisect; // Debug aid
|
||||
|
|
@ -404,6 +407,12 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return make<Vertex>(examplep->fileline(), examplep->dtype(), operands...);
|
||||
}
|
||||
|
||||
// Replicate 'bitp' to 'vtxp->width()' bits
|
||||
DfgVertex* replicate(DfgVertex* vtxp, DfgVertex* bitp) {
|
||||
if (vtxp->dtype() == m_bitDType) return bitp;
|
||||
return make<DfgReplicate>(vtxp, bitp, makeI32(vtxp->fileline(), vtxp->width()));
|
||||
}
|
||||
|
||||
// Check two vertex are the same, or the same constant value
|
||||
static bool isSame(const DfgVertex* ap, const DfgVertex* bp) {
|
||||
if (ap == bp) return true;
|
||||
|
|
@ -561,15 +570,25 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Attempt to reuse associative binary expressions if hey already exist, e.g.:
|
||||
// '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or
|
||||
// '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative.
|
||||
// Only do this is 'b OP c' has a single use and can subsequently be removed,
|
||||
// otherwise there is no improvement.
|
||||
if (rSamep && !rSamep->hasMultipleSinks()) {
|
||||
DfgVertex* const rlVtxp = rSamep->lhsp();
|
||||
DfgVertex* const rrVtxp = rSamep->rhsp();
|
||||
|
||||
if VL_CONSTEXPR_CXX17 (IsCommutative<Vertex>::value) {
|
||||
if (!lhsp->hasMultipleSinks() && rlVtxp->hasMultipleSinks()) {
|
||||
APPLYING(ROTATE_ASSOC_COMM_MULTIUSE) {
|
||||
replace(make<Vertex>(vtxp, rlVtxp, make<Vertex>(vtxp, lhsp, rrVtxp)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to reuse associative binary expressions if hey already exist, e.g.:
|
||||
// '(a OP (b OP c))' -> '(a OP b) OP c', iff '(a OP b)' already exists, or
|
||||
// '(a OP c) OP b' iff '(a OP c)' already exists and the vertex is commutative.
|
||||
// Only do this if 'b OP c' has a single use and can subsequently be removed,
|
||||
// otherwise there is no improvement.
|
||||
|
||||
// '(a OP (b OP c))' -> '(a OP b) OP c'
|
||||
if (Vertex* const existingp
|
||||
= m_cache.get<Vertex>(resultDType<Vertex>(lhsp, rlVtxp), lhsp, rlVtxp)) {
|
||||
|
|
@ -700,10 +719,10 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
tryPushBitwiseOpThroughConcat(Vertex* const vtxp, DfgConst* constp, DfgConcat* concatp) {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
|
||||
// If at least one of the sides of the Concat constant, or width 1 (i.e.: can be
|
||||
// further simplified), then push the Vertex past the Concat
|
||||
if (concatp->lhsp()->is<DfgConst>() || concatp->rhsp()->is<DfgConst>() //
|
||||
|| concatp->lhsp()->dtype() == m_bitDType || concatp->rhsp()->dtype() == m_bitDType) {
|
||||
// If at least one of the sides of the Concat constant, then push Vertex past Concat
|
||||
DfgConst* const catLConstp = concatp->lhsp()->cast<DfgConst>();
|
||||
DfgConst* const catRConstp = concatp->rhsp()->cast<DfgConst>();
|
||||
if (catLConstp || catRConstp) {
|
||||
APPLYING(PUSH_BITWISE_OP_THROUGH_CONCAT) {
|
||||
const uint32_t width = concatp->width();
|
||||
const DfgDataType& lDtype = concatp->lhsp()->dtype();
|
||||
|
|
@ -712,14 +731,30 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
const uint32_t rWidth = rDtype.size();
|
||||
|
||||
// The new Lhs vertex
|
||||
DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth);
|
||||
newLhsConstp->num().opSel(constp->num(), width - 1, rWidth);
|
||||
Vertex* const newLhsp = make<Vertex>(flp, lDtype, newLhsConstp, concatp->lhsp());
|
||||
DfgVertex* const newLhsp = [&]() -> DfgVertex* {
|
||||
DfgConst* const newLhsConstp = makeZero(constp->fileline(), lWidth);
|
||||
if (catLConstp) {
|
||||
V3Number num{constp->fileline(), static_cast<int>(lWidth), 0u};
|
||||
num.opSel(constp->num(), width - 1, rWidth);
|
||||
foldOp<Vertex>(newLhsConstp->num(), num, catLConstp->num());
|
||||
return newLhsConstp;
|
||||
}
|
||||
newLhsConstp->num().opSel(constp->num(), width - 1, rWidth);
|
||||
return make<Vertex>(flp, lDtype, newLhsConstp, concatp->lhsp());
|
||||
}();
|
||||
|
||||
// The new Rhs vertex
|
||||
DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth);
|
||||
newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0);
|
||||
Vertex* const newRhsp = make<Vertex>(flp, rDtype, newRhsConstp, concatp->rhsp());
|
||||
DfgVertex* const newRhsp = [&]() -> DfgVertex* {
|
||||
DfgConst* const newRhsConstp = makeZero(constp->fileline(), rWidth);
|
||||
if (catRConstp) {
|
||||
V3Number num{constp->fileline(), static_cast<int>(rWidth), 0u};
|
||||
num.opSel(constp->num(), rWidth - 1, 0);
|
||||
foldOp<Vertex>(newRhsConstp->num(), num, catRConstp->num());
|
||||
return newRhsConstp;
|
||||
}
|
||||
newRhsConstp->num().opSel(constp->num(), rWidth - 1, 0);
|
||||
return make<Vertex>(flp, rDtype, newRhsConstp, concatp->rhsp());
|
||||
}();
|
||||
|
||||
// Replace this vertex
|
||||
replace(make<DfgConcat>(concatp, newLhsp, newRhsp));
|
||||
|
|
@ -794,6 +829,46 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename Bitwise>
|
||||
VL_ATTR_WARN_UNUSED_RESULT bool tryPushBitwiseOpThrougSel(Bitwise* const vtxp) {
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
DfgVertex* const rhsp = vtxp->rhsp();
|
||||
|
||||
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
|
||||
DfgSel* rSelp = nullptr;
|
||||
DfgVertex* extrap = nullptr;
|
||||
if (DfgSel* const selp = rhsp->cast<DfgSel>()) {
|
||||
rSelp = selp;
|
||||
} else if (Bitwise* const bitwisep = rhsp->cast<Bitwise>()) {
|
||||
if (DfgSel* const rlSelp = bitwisep->lhsp()->template cast<DfgSel>()) {
|
||||
rSelp = rlSelp;
|
||||
extrap = bitwisep->rhsp();
|
||||
} else if (DfgSel* const rrSelp = bitwisep->rhsp()->template cast<DfgSel>()) {
|
||||
rSelp = rrSelp;
|
||||
extrap = bitwisep->lhsp();
|
||||
}
|
||||
}
|
||||
if (rSelp) {
|
||||
DfgVertex* const lFromp = lSelp->fromp();
|
||||
DfgVertex* const rFromp = rSelp->fromp();
|
||||
if (lFromp->dtype() == rFromp->dtype() && lFromp->width() <= VL_QUADSIZE //
|
||||
&& lSelp->lsb() == rSelp->lsb()) {
|
||||
APPLYING(PUSH_BITWISE_THROUGH_SEL) {
|
||||
Bitwise* const bwp
|
||||
= make<Bitwise>(vtxp->fileline(), lSelp->fromp()->dtype(),
|
||||
lSelp->fromp(), rSelp->fromp());
|
||||
DfgVertex* resp = make<DfgSel>(vtxp, bwp, lSelp->lsb());
|
||||
if (extrap) resp = make<Bitwise>(vtxp, resp, extrap);
|
||||
replace(resp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Bitwise>
|
||||
VL_ATTR_WARN_UNUSED_RESULT bool tryReplaceBitwiseWithReduction(Bitwise* vtxp) {
|
||||
UASSERT_OBJ(vtxp->width() == 1, vtxp, "Width must be 1");
|
||||
|
|
@ -803,13 +878,15 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
DfgVertex* const rhsp = vtxp->rhsp();
|
||||
|
||||
if (DfgSel* const lSelp = lhsp->template cast<DfgSel>()) {
|
||||
DfgSel* rSelp = rhsp->template cast<DfgSel>();
|
||||
DfgSel* rSelp = nullptr;
|
||||
DfgVertex* extrap = nullptr;
|
||||
if (!rSelp) {
|
||||
if (Bitwise* const rBitwisep = rhsp->template cast<Bitwise>()) {
|
||||
rSelp = rBitwisep->lhsp()->template cast<DfgSel>();
|
||||
extrap = rBitwisep->rhsp();
|
||||
}
|
||||
if (DfgSel* const selp = rhsp->template cast<DfgSel>()) {
|
||||
rSelp = selp;
|
||||
} else if (Bitwise* const rBitwisep = rhsp->template cast<Bitwise>()) {
|
||||
rSelp = rBitwisep->lhsp()->template cast<DfgSel>();
|
||||
extrap = rBitwisep->rhsp();
|
||||
} else if (Reduction* const rRedp = rhsp->template cast<Reduction>()) {
|
||||
rSelp = rRedp->srcp()->template cast<DfgSel>();
|
||||
}
|
||||
if (rSelp) {
|
||||
uint32_t lsb = 0;
|
||||
|
|
@ -1021,6 +1098,94 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return {nullptr, 0, 0};
|
||||
}
|
||||
|
||||
// The following patterns all unwind a Sel throgh it's source and replace it with
|
||||
// another single Sel. Doing this one at a time can take a long time with nested
|
||||
// concatenations/selects/etc, so instead unwind as much as possible in one go.
|
||||
std::pair<DfgVertex*, uint32_t> unwindSel(DfgVertex* fromp, uint32_t lsb,
|
||||
const uint32_t width) {
|
||||
while (true) {
|
||||
const uint32_t msb = lsb + width - 1;
|
||||
|
||||
// Sel from Concat
|
||||
if (DfgConcat* const concatp = fromp->cast<DfgConcat>()) {
|
||||
DfgVertex* const lhsp = concatp->lhsp();
|
||||
DfgVertex* const rhsp = concatp->rhsp();
|
||||
|
||||
if (msb < rhsp->width()) {
|
||||
// If the select is entirely from rhs, then replace with sel from rhs
|
||||
APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) {
|
||||
fromp = rhsp;
|
||||
continue;
|
||||
}
|
||||
} else if (lsb >= rhsp->width()) {
|
||||
// If the select is entirely from the lhs, then replace with sel from lhs
|
||||
APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) {
|
||||
fromp = lhsp;
|
||||
lsb -= rhsp->width();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DfgReplicate* const repp = fromp->cast<DfgReplicate>()) {
|
||||
// If the Sel is wholly into the source of the Replicate, push the Sel through
|
||||
// the Replicate and apply it directly to the source of the Replicate.
|
||||
const uint32_t srcWidth = repp->srcp()->width();
|
||||
if (width <= srcWidth) {
|
||||
const uint32_t newLsb = lsb % srcWidth;
|
||||
const uint32_t newMsb = newLsb + width - 1;
|
||||
if (newMsb < srcWidth) {
|
||||
APPLYING(PUSH_SEL_THROUGH_REPLICATE) {
|
||||
fromp = repp->srcp();
|
||||
lsb = newLsb;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sel from Sel
|
||||
if (DfgSel* const selp = fromp->cast<DfgSel>()) {
|
||||
APPLYING(REPLACE_SEL_FROM_SEL) {
|
||||
// Select from the source of the source Sel with adjusted LSB
|
||||
fromp = selp->fromp();
|
||||
lsb += selp->lsb();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sel from a partial variable (including narrowed vertex)
|
||||
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
|
||||
if (varp->srcp() && !varp->isVolatile()) {
|
||||
// Must be a splice, otherwise it would have been inlined
|
||||
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
|
||||
DfgVertex* driverp = nullptr;
|
||||
uint32_t driverLsb = 0;
|
||||
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
|
||||
const uint32_t dMsb = dLsb + src.width() - 1;
|
||||
// If it does not cover the whole searched bit range, move on
|
||||
if (lsb < dLsb || dMsb < msb) return false;
|
||||
// Save the driver
|
||||
driverp = &src;
|
||||
driverLsb = dLsb;
|
||||
return true;
|
||||
});
|
||||
if (driverp) {
|
||||
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
|
||||
fromp = driverp;
|
||||
lsb -= driverLsb;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No patterns matched, stop
|
||||
break;
|
||||
}
|
||||
return {fromp, lsb};
|
||||
}
|
||||
|
||||
// VISIT methods
|
||||
|
||||
void visit(DfgVertex*) override {}
|
||||
|
|
@ -1148,38 +1313,12 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Sel from Concat
|
||||
if (DfgConcat* const concatp = fromp->cast<DfgConcat>()) {
|
||||
DfgVertex* const lhsp = concatp->lhsp();
|
||||
DfgVertex* const rhsp = concatp->rhsp();
|
||||
|
||||
if (msb < rhsp->width()) {
|
||||
// If the select is entirely from rhs, then replace with sel from rhs
|
||||
APPLYING(REMOVE_SEL_FROM_RHS_OF_CONCAT) { //
|
||||
replace(make<DfgSel>(vtxp, rhsp, vtxp->lsb()));
|
||||
return;
|
||||
}
|
||||
} else if (lsb >= rhsp->width()) {
|
||||
// If the select is entirely from the lhs, then replace with sel from lhs
|
||||
APPLYING(REMOVE_SEL_FROM_LHS_OF_CONCAT) {
|
||||
replace(make<DfgSel>(vtxp, lhsp, lsb - rhsp->width()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DfgReplicate* const repp = fromp->cast<DfgReplicate>()) {
|
||||
// If the Sel is wholly into the source of the Replicate, push the Sel through the
|
||||
// Replicate and apply it directly to the source of the Replicate.
|
||||
const uint32_t srcWidth = repp->srcp()->width();
|
||||
if (width <= srcWidth) {
|
||||
const uint32_t newLsb = lsb % srcWidth;
|
||||
if (newLsb + width <= srcWidth) {
|
||||
APPLYING(PUSH_SEL_THROUGH_REPLICATE) {
|
||||
replace(make<DfgSel>(vtxp, repp->srcp(), newLsb));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Unwind through bit packing in one go
|
||||
{
|
||||
const auto res = unwindSel(fromp, lsb, width);
|
||||
if (res.first != fromp || res.second != lsb) {
|
||||
replace(make<DfgSel>(vtxp, res.first, res.second));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1197,15 +1336,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Sel from Sel
|
||||
if (DfgSel* const selp = fromp->cast<DfgSel>()) {
|
||||
APPLYING(REPLACE_SEL_FROM_SEL) {
|
||||
// Select from the source of the source Sel with adjusted LSB
|
||||
replace(make<DfgSel>(vtxp, selp->fromp(), lsb + selp->lsb()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sel from Cond
|
||||
if (DfgCond* const condp = fromp->cast<DfgCond>()) {
|
||||
if (!condp->hasMultipleSinks()) {
|
||||
|
|
@ -1239,31 +1369,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sel from a partial variable (including narrowed vertex)
|
||||
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
|
||||
if (varp->srcp() && !varp->isVolatile()) {
|
||||
// Must be a splice, otherwise it would have been inlined
|
||||
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
|
||||
DfgVertex* driverp = nullptr;
|
||||
uint32_t driverLsb = 0;
|
||||
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
|
||||
const uint32_t dMsb = dLsb + src.width() - 1;
|
||||
// If it does not cover the whole searched bit range, move on
|
||||
if (lsb < dLsb || dMsb < msb) return false;
|
||||
// Save the driver
|
||||
driverp = &src;
|
||||
driverLsb = dLsb;
|
||||
return true;
|
||||
});
|
||||
if (driverp) {
|
||||
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
|
||||
replace(make<DfgSel>(vtxp, driverp, lsb - driverLsb));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(DfgMux* const vtxp) override {
|
||||
|
|
@ -1349,18 +1454,27 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
|
||||
|
||||
if (DfgNot* const lhsNotp = lhsp->cast<DfgNot>()) {
|
||||
if (tryPushBitwiseOpThrougSel(vtxp)) return;
|
||||
|
||||
{
|
||||
DfgNot* const lNotp = lhsp->cast<DfgNot>();
|
||||
DfgNot* const rNotp = rhsp->cast<DfgNot>();
|
||||
// ~A & A is all zeroes
|
||||
if (lhsNotp->srcp() == rhsp) {
|
||||
if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) {
|
||||
APPLYING(REPLACE_CONTRADICTORY_AND) {
|
||||
replace(makeZero(flp, vtxp->width()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ~A & (A & _) or ~A & (_ & A) is all zeroes
|
||||
if (DfgAnd* const rhsAndp = rhsp->cast<DfgAnd>()) {
|
||||
if (lhsNotp->srcp() == rhsAndp->lhsp() || lhsNotp->srcp() == rhsAndp->rhsp()) {
|
||||
if (DfgAnd* const rSamep = rhsp->cast<DfgAnd>()) {
|
||||
DfgNot* const rlNotp = rSamep->lhsp()->cast<DfgNot>();
|
||||
DfgNot* const rrNotp = rSamep->rhsp()->cast<DfgNot>();
|
||||
// ~A & (A & _) or ~A & (_ & A) is all zeroes
|
||||
if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp()))
|
||||
|| (lNotp && isSame(lNotp->srcp(), rSamep->rhsp()))
|
||||
|| (rlNotp && isSame(lhsp, rlNotp->srcp()))
|
||||
|| (rrNotp && isSame(lhsp, rrNotp->srcp()))) {
|
||||
APPLYING(REPLACE_CONTRADICTORY_AND_3) {
|
||||
replace(makeZero(flp, vtxp->width()));
|
||||
return;
|
||||
|
|
@ -1463,24 +1577,29 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
|
||||
|
||||
if (DfgNot* const lhsNotp = lhsp->cast<DfgNot>()) {
|
||||
if (tryPushBitwiseOpThrougSel(vtxp)) return;
|
||||
|
||||
{
|
||||
DfgNot* const lNotp = lhsp->cast<DfgNot>();
|
||||
DfgNot* const rNotp = rhsp->cast<DfgNot>();
|
||||
// ~A | A is all ones
|
||||
if (lhsNotp->srcp() == rhsp) {
|
||||
if ((lNotp && isSame(lNotp->srcp(), rhsp)) || (rNotp && isSame(lhsp, rNotp->srcp()))) {
|
||||
APPLYING(REPLACE_TAUTOLOGICAL_OR) {
|
||||
DfgConst* const resp = makeZero(flp, vtxp->width());
|
||||
resp->num().setAllBits1();
|
||||
replace(resp);
|
||||
replace(makeOnes(flp, vtxp->width()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ~A | (A | _) or ~A | (_ | A) is all ones
|
||||
if (DfgOr* const rhsOrp = rhsp->cast<DfgOr>()) {
|
||||
if (lhsNotp->srcp() == rhsOrp->lhsp() || lhsNotp->srcp() == rhsOrp->rhsp()) {
|
||||
if (DfgOr* const rSamep = rhsp->cast<DfgOr>()) {
|
||||
DfgNot* const rlNotp = rSamep->lhsp()->cast<DfgNot>();
|
||||
DfgNot* const rrNotp = rSamep->rhsp()->cast<DfgNot>();
|
||||
// ~A | (A | _) or ~A | (_ | A) is all ones
|
||||
if ((lNotp && isSame(lNotp->srcp(), rSamep->lhsp()))
|
||||
|| (lNotp && isSame(lNotp->srcp(), rSamep->rhsp()))
|
||||
|| (rlNotp && isSame(lhsp, rlNotp->srcp()))
|
||||
|| (rrNotp && isSame(lhsp, rrNotp->srcp()))) {
|
||||
APPLYING(REPLACE_TAUTOLOGICAL_OR_3) {
|
||||
DfgConst* const resp = makeZero(flp, vtxp->width());
|
||||
resp->num().setAllBits1();
|
||||
replace(resp);
|
||||
replace(makeOnes(flp, vtxp->width()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1525,6 +1644,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
if (tryPushBitwiseOpThroughReductions(vtxp)) return;
|
||||
|
||||
if (tryPushBitwiseOpThrougSel(vtxp)) return;
|
||||
|
||||
if (vtxp->dtype() == m_bitDType) {
|
||||
if (tryReplaceBitwiseWithReduction(vtxp)) return;
|
||||
}
|
||||
|
|
@ -1847,10 +1968,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
|
||||
m_vInfo[sp].m_id = ++m_lastId;
|
||||
sp->addDriver(catp, lsb, flp);
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
AstScope* const scopep = vtxp->scopep(scopeCache, true);
|
||||
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
|
||||
DfgVertexVar* const varp
|
||||
= m_dfg.makeNewVar(flp, name, vtxp->dtype(), m_tmpScopep);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_vInfo[varp].m_id = ++m_lastId;
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
|
|
@ -2511,53 +2631,63 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
if (vtxp->dtype() == m_bitDType) {
|
||||
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
||||
replace(make<DfgAnd>(vtxp, make<DfgNot>(vtxp, condp), elsep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (thenp == condp) { // a ? a : b becomes a | b
|
||||
if (isSame(condp, thenp)) { // a ? a : b becomes a | b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) {
|
||||
replace(make<DfgOr>(vtxp, condp, elsep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (elsep == condp) { // a ? b : a becomes a & b
|
||||
if (isSame(condp, elsep)) { // a ? b : a becomes a & b
|
||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) {
|
||||
replace(make<DfgAnd>(vtxp, condp, thenp));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vtxp->width() <= VL_QUADSIZE) {
|
||||
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||
replace(make<DfgAnd>(vtxp, maskp, elsep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isOnes(thenp)) { // a ? 1 : b becomes a | b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
|
||||
replace(make<DfgOr>(vtxp, condp, elsep));
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
replace(make<DfgOr>(vtxp, maskp, elsep));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isZero(elsep)) { // a ? b : 0 becomes a & b
|
||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
|
||||
replace(make<DfgAnd>(vtxp, condp, thenp));
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
replace(make<DfgAnd>(vtxp, maskp, thenp));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
|
||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
|
||||
replace(make<DfgOr>(vtxp, make<DfgNot>(vtxp, condp), thenp));
|
||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||
replace(make<DfgOr>(vtxp, maskp, thenp));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (DfgOr* const tOrp = thenp->cast<DfgOr>()) {
|
||||
if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c)
|
||||
APPLYING(REPLACE_COND_THEN_OR_LHS) {
|
||||
DfgAnd* const andp = make<DfgAnd>(vtxp, condp, tOrp->rhsp());
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
|
||||
replace(make<DfgOr>(vtxp, tOrp->lhsp(), andp));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b)
|
||||
APPLYING(REPLACE_COND_THEN_OR_RHS) {
|
||||
DfgAnd* const andp = make<DfgAnd>(vtxp, condp, tOrp->lhsp());
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->lhsp());
|
||||
replace(make<DfgOr>(vtxp, tOrp->rhsp(), andp));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2590,6 +2720,33 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tConcatp->hasMultipleSinks()) {
|
||||
if (DfgConcat* const tRCatp = tConcatp->rhsp()->cast<DfgConcat>()) {
|
||||
if (!tRCatp->hasMultipleSinks()) {
|
||||
if (DfgSel* const tLSelp = tConcatp->lhsp()->cast<DfgSel>()) {
|
||||
if (DfgSel* const tRRSelp = tRCatp->rhsp()->cast<DfgSel>()) {
|
||||
if (tLSelp->lsb() == tRCatp->width() //
|
||||
&& tRRSelp->lsb() == 0 //
|
||||
&& isSame(tLSelp->fromp(), elsep) //
|
||||
&& isSame(tRRSelp->fromp(), elsep)) {
|
||||
APPLYING(REPLACE_COND_INSERT) {
|
||||
DfgVertex* const newTp = tRCatp->lhsp();
|
||||
DfgVertex* const newEp = make<DfgSel>(
|
||||
flp, newTp->dtype(), elsep, tRRSelp->width());
|
||||
DfgCond* const newCp = make<DfgCond>(flp, newTp->dtype(),
|
||||
condp, newTp, newEp);
|
||||
replace(make<DfgConcat>(
|
||||
vtxp, tLSelp,
|
||||
make<DfgConcat>(tRCatp, newCp, tRRSelp)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEqOne(thenp) && isZero(elsep)) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PULL_NOTS_THROUGH_COND) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_OP_THROUGH_CONCAT) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_REDUCTION) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_BITWISE_THROUGH_SEL) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMMUTATIVE_BINARY_THROUGH_COND) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_COMPARE_OP_THROUGH_CONCAT) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, PUSH_CONCAT_THROUGH_COND_LHS) \
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_CONST_ZERO_ONES) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_DEC) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_INC) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_INSERT) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_LHS) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_OR_THEN_COND_RHS) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REPLACE_COND_SAME_CAT_LHS) \
|
||||
|
|
@ -160,6 +162,7 @@
|
|||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_LHS_OF_RHS) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, REUSE_ASSOC_BINARY_LHS_WITH_RHS_OF_RHS) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, RIGHT_LEANING_ASSOC) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, ROTATE_ASSOC_COMM_MULTIUSE) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NEQ_CONDITION) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_COND_WITH_NOT_CONDITION) \
|
||||
_FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION_APPLY(macro, SWAP_SIDES_IN_BINARY)
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
if (nodep->sensp()) puts(" ");
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstCReset* /*nodep*/) override { puts("/*CRESET*/"); }
|
||||
void visit(AstCReset* nodep) override { puts("/*CRESET*/"); }
|
||||
void visit(AstCase* nodep) override {
|
||||
putfs(nodep, "");
|
||||
if (nodep->priorityPragma()) puts("priority ");
|
||||
|
|
@ -307,6 +307,72 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
void visit(AstCoverInc*) override {} // N/A
|
||||
void visit(AstCoverToggle*) override {} // N/A
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// AstCovergroup appears as a member inside the lowered AstClass body.
|
||||
// The outer covergroup/endcovergroup wrapper is already emitted by the
|
||||
// AstNodeModule visitor (verilogKwd()="covergroup" on AstClass::isCovergroup).
|
||||
// Here we only emit the clocking event, if any.
|
||||
if (nodep->eventp()) {
|
||||
putfs(nodep, "");
|
||||
iterateConst(nodep->eventp());
|
||||
}
|
||||
}
|
||||
void visit(AstCoverpoint* nodep) override {
|
||||
putfs(nodep, nodep->name() + ": coverpoint ");
|
||||
iterateAndNextConstNull(nodep->exprp());
|
||||
if (nodep->binsp() || nodep->optionsp()) {
|
||||
puts(" {\n");
|
||||
iterateAndNextConstNull(nodep->optionsp());
|
||||
iterateAndNextConstNull(nodep->binsp());
|
||||
puts("}");
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverBin* nodep) override {
|
||||
switch (nodep->binsType()) {
|
||||
case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break;
|
||||
case VCoverBinsType::BINS_ILLEGAL: putfs(nodep, "illegal_bins "); break;
|
||||
default: putfs(nodep, "bins "); break;
|
||||
}
|
||||
puts(nodep->name());
|
||||
if (nodep->binsType() == VCoverBinsType::BINS_DEFAULT) {
|
||||
puts(" = default");
|
||||
} else if (nodep->transp()) {
|
||||
puts(" = ");
|
||||
for (AstNode* setp = nodep->transp(); setp; setp = setp->nextp()) {
|
||||
if (setp != nodep->transp()) puts(", ");
|
||||
iterateConst(setp);
|
||||
}
|
||||
} else if (nodep->rangesp()) { // LCOV_EXCL_BR_LINE - false: CoverBin always has
|
||||
// transp/rangesp/default
|
||||
puts(" = {");
|
||||
for (AstNode* rangep = nodep->rangesp(); rangep; rangep = rangep->nextp()) {
|
||||
if (rangep != nodep->rangesp()) puts(", ");
|
||||
iterateConst(rangep);
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverpointRef* nodep) override { putfs(nodep, nodep->name()); }
|
||||
void visit(AstCoverCross* nodep) override {
|
||||
putfs(nodep, nodep->name() + ": cross ");
|
||||
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp != nodep->itemsp()) puts(", ");
|
||||
iterateConst(itemp);
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverTransSet* nodep) override {
|
||||
puts("(");
|
||||
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp != nodep->itemsp()) puts(" => ");
|
||||
iterateConst(itemp);
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstCoverTransItem* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
void visit(AstCvtPackString* nodep) override {
|
||||
putfs(nodep, "");
|
||||
if (AstConst* const lhsConstp = VN_CAST(nodep->lhsp(), Const)) {
|
||||
|
|
@ -745,6 +811,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
iterateAndNextConstNull(nodep->elsep());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstInsideRange* nodep) override {
|
||||
puts("[");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(":");
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
puts("]");
|
||||
}
|
||||
void visit(AstRange* nodep) override {
|
||||
puts("[");
|
||||
if (VN_IS(nodep->leftp(), Const) && VN_IS(nodep->rightp(), Const)) {
|
||||
|
|
@ -942,6 +1015,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
|
||||
}
|
||||
}
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
UASSERT_OBJ(nodep->classp(), nodep, "AstClassRefDType not linked");
|
||||
putfs(nodep, EmitCUtil::prefixNameProtect(nodep->classp()));
|
||||
}
|
||||
void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); }
|
||||
void visit(AstModport* nodep) override {
|
||||
puts(nodep->verilogKwd());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,246 @@ 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, AstFunc* newFuncp, AstNode* argsp,
|
||||
AstNode* sampleArgsp) {
|
||||
// Hidden static to take unspecified reference argument results
|
||||
AstVar* const defaultVarp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
|
||||
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
nodep->addStmtsp(defaultVarp);
|
||||
|
||||
// Handle constructor arguments - add function parameters and assignments
|
||||
if (argsp) {
|
||||
UASSERT_OBJ(newFuncp, nodep,
|
||||
"Covergroup class must have a 'new' constructor function");
|
||||
// Save the existing body statements and unlink them
|
||||
AstNode* const existingBodyp = newFuncp->stmtsp();
|
||||
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
|
||||
// Add function parameters and assignments
|
||||
for (AstNode* argp = argsp; argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const paramp = origVarp->cloneTree(false);
|
||||
paramp->funcLocal(true);
|
||||
paramp->direction(VDirection::INPUT);
|
||||
newFuncp->addStmtsp(paramp);
|
||||
AstNodeExpr* const lhsp = new AstParseRef{origVarp->fileline(), origVarp->name()};
|
||||
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
|
||||
newFuncp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
|
||||
}
|
||||
if (existingBodyp) newFuncp->addStmtsp(existingBodyp);
|
||||
}
|
||||
|
||||
// IEEE: option / type_option members allow external access (cg_inst.option.X)
|
||||
// and require std:: types; std:: is kept because setUsesStdPackage() is called
|
||||
// at parse time for every covergroup declaration.
|
||||
{
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
{
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: function void sample([arguments])
|
||||
{
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
|
||||
if (sampleArgsp) {
|
||||
for (AstNode* argp = sampleArgsp; argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const paramp = origVarp->cloneTree(false);
|
||||
paramp->funcLocal(true);
|
||||
paramp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(paramp);
|
||||
AstNodeExpr* const lhsp
|
||||
= new AstParseRef{origVarp->fileline(), origVarp->name()};
|
||||
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
|
||||
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
|
||||
}
|
||||
}
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: function void start(), void stop()
|
||||
for (const string& name : {"start"s, "stop"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: static function real get_coverage(optional ref int, optional ref int)
|
||||
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
|
||||
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
funcp->isStatic(name == "get_coverage");
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
{
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
|
||||
nodep->findDoubleDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::OUTPUT);
|
||||
varp->funcReturn(true);
|
||||
funcp->fvarp(varp);
|
||||
}
|
||||
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
|
||||
// IEEE: function void set_inst_name(string)
|
||||
{
|
||||
AstFunc* const funcp
|
||||
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstCovergroup* nodep) override {
|
||||
// AstCovergroup can only appear inside a module/class/package; never at root level.
|
||||
UASSERT_OBJ(m_modp, nodep, "AstCovergroup not under module");
|
||||
// If we're already inside a covergroup class, this is the sentinel AstCovergroup
|
||||
// node carrying the clocking event for V3Covergroup - don't re-transform it.
|
||||
if (VN_IS(m_modp, Class) && VN_AS(m_modp, Class)->isCovergroup()) return;
|
||||
|
||||
// Transform raw parse-time AstCovergroup into a fully-formed AstClass
|
||||
cleanFileline(nodep);
|
||||
|
||||
const string libname = m_modp->libname();
|
||||
AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname};
|
||||
cgClassp->isCovergroup(true);
|
||||
v3Global.useCovergroup(true);
|
||||
|
||||
// Clocking event: unlink before deleteTree, attach as AstCovergroup child on class
|
||||
if (AstSenTree* const eventp = nodep->eventp()) {
|
||||
eventp->unlinkFrBack();
|
||||
AstCovergroup* const cgNodep = new AstCovergroup{
|
||||
nodep->fileline(), nodep->name(), nullptr, nullptr, nullptr, eventp};
|
||||
cgClassp->addMembersp(cgNodep);
|
||||
}
|
||||
|
||||
// Convert constructor args to member variables
|
||||
for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const memberp = origVarp->cloneTree(false);
|
||||
memberp->varType(VVarType::MEMBER);
|
||||
memberp->funcLocal(false);
|
||||
memberp->direction(VDirection::NONE);
|
||||
cgClassp->addMembersp(memberp);
|
||||
}
|
||||
|
||||
// Convert sample args to member variables
|
||||
for (AstNode* argp = nodep->sampleArgsp(); argp; argp = argp->nextp()) {
|
||||
AstVar* const origVarp = VN_AS(argp, Var);
|
||||
AstVar* const memberp = origVarp->cloneTree(false);
|
||||
memberp->varType(VVarType::MEMBER);
|
||||
memberp->funcLocal(false);
|
||||
memberp->direction(VDirection::NONE);
|
||||
cgClassp->addMembersp(memberp);
|
||||
}
|
||||
|
||||
// Create the constructor; detach membersp (coverage body) and use as its body
|
||||
AstFunc* const newFuncp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
|
||||
newFuncp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newFuncp->classMethod(true);
|
||||
newFuncp->isConstructor(true);
|
||||
newFuncp->dtypep(cgClassp->dtypep());
|
||||
if (AstNode* const bodyp = nodep->membersp()) {
|
||||
bodyp->unlinkFrBackWithNext();
|
||||
newFuncp->addStmtsp(bodyp);
|
||||
}
|
||||
cgClassp->addMembersp(newFuncp);
|
||||
|
||||
// Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep)
|
||||
createCovergroupMethods(cgClassp, newFuncp, nodep->argsp(), nodep->sampleArgsp());
|
||||
|
||||
// Replace AstCovergroup with AstClass and process the new class normally
|
||||
nodep->replaceWith(cgClassp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(cgClassp);
|
||||
}
|
||||
|
||||
void visit(AstCoverpoint* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
// Re-sort the parse-time mixed bins list (AstCoverBin + AstCgOptionAssign)
|
||||
// into the typed binsp and optionsp slots. The grammar attaches both node types
|
||||
// to binsp (op2) as a raw List[AstNode]; now that they are properly parented we
|
||||
// can iterate and split them without any temporary-parent tricks.
|
||||
for (AstNode *itemp = nodep->binsp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = itemp->nextp();
|
||||
if (AstCgOptionAssign* const optp = VN_CAST(itemp, CgOptionAssign)) {
|
||||
optp->unlinkFrBack();
|
||||
if (optp->optionType() == VCoverOptionType::UNKNOWN) {
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage option: " + optp->prettyNameQ());
|
||||
} else {
|
||||
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optionType(),
|
||||
optp->valuep()->cloneTree(false)});
|
||||
}
|
||||
VL_DO_DANGLING(optp->deleteTree(), optp);
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCoverCross* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
// Distribute the parse-time raw cross_body list (rawBodyp, op3) into the
|
||||
// typed optionsp slot. The grammar produces AstCgOptionAssign nodes for
|
||||
// option.* items; convert them to AstCoverOption exactly as visit(AstCoverpoint*)
|
||||
// does. Other items (functions, unsupported bin selectors) are discarded.
|
||||
for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = itemp->nextp();
|
||||
itemp->unlinkFrBack();
|
||||
AstCgOptionAssign* const optp = VN_AS(itemp, CgOptionAssign);
|
||||
const VCoverOptionType optType = optp->optionType();
|
||||
if (!(optType == VCoverOptionType::AT_LEAST || optType == VCoverOptionType::WEIGHT
|
||||
|| optType == VCoverOptionType::GOAL || optType == VCoverOptionType::COMMENT)) {
|
||||
optp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported coverage cross option: " + optp->prettyNameQ());
|
||||
}
|
||||
// Always preserve the option node so V3Coverage can track its source line
|
||||
// for coverage annotation, even when the option itself is unsupported.
|
||||
nodep->addOptionsp(
|
||||
new AstCoverOption{optp->fileline(), optType, optp->valuep()->cloneTree(false)});
|
||||
VL_DO_DANGLING(optp->deleteTree(), optp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
// Default: Just iterate
|
||||
cleanFileline(nodep);
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst {
|
|||
}
|
||||
|
||||
// VISITORS
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
// Push a new stack entry at the start of a list, but only if the list is not a
|
||||
// single element (this saves a lot of allocations in expressions)
|
||||
|
|
|
|||
|
|
@ -1406,13 +1406,16 @@ class ParamProcessor final {
|
|||
}
|
||||
cloneVarp->valuep(exprp->cloneTree(false));
|
||||
if (AstNodeDType* const origDTypep = modvarp->subDTypep()) {
|
||||
AstNodeDType* const dtypeClonep = origDTypep->cloneTree(false);
|
||||
// Inline every param ref so widthing doesn't reach back into the template
|
||||
// (#7411). Cycle detector for dependent parameters in the same module.
|
||||
// Attach clone under cloneVarp so the root has a back pointer.
|
||||
if (cloneVarp->childDTypep())
|
||||
cloneVarp->childDTypep()->unlinkFrBack()->deleteTree();
|
||||
cloneVarp->childDTypep(origDTypep->cloneTree(false));
|
||||
cloneVarp->dtypep(nullptr);
|
||||
// Inline param refs so widthing doesn't touch the template (#7411).
|
||||
constexpr int maxSubstIters = 1000;
|
||||
for (int it = 0; it < maxSubstIters; ++it) {
|
||||
bool any = false;
|
||||
dtypeClonep->foreach([&](AstVarRef* varrefp) {
|
||||
cloneVarp->foreach([&](AstVarRef* varrefp) {
|
||||
AstVar* const targetp = varrefp->varp();
|
||||
AstNode* replacep = nullptr;
|
||||
for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) {
|
||||
|
|
@ -1432,19 +1435,40 @@ class ParamProcessor final {
|
|||
any = true;
|
||||
}
|
||||
});
|
||||
// Substitute RefDType to an overridden paramtype. RefDType is
|
||||
// not a foreach leaf, so collect matches and replace after the
|
||||
// walk. Reverse order so descendants are replaced before
|
||||
// ancestors -- replacing an ancestor would free its descendants.
|
||||
std::vector<std::pair<AstRefDType*, AstNodeDType*>> toReplace;
|
||||
cloneVarp->foreach([&](AstRefDType* refp) {
|
||||
AstParamTypeDType* const ptdp
|
||||
= VN_CAST(refp->refDTypep(), ParamTypeDType);
|
||||
if (!ptdp) return;
|
||||
for (AstPin* pp = paramsp; pp; pp = VN_AS(pp->nextp(), Pin)) {
|
||||
if (pp->modPTypep() == ptdp) {
|
||||
if (AstNodeDType* const overDtp
|
||||
= VN_CAST(pp->exprp(), NodeDType)) {
|
||||
toReplace.emplace_back(refp, overDtp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
for (auto it = toReplace.rbegin(); it != toReplace.rend(); ++it) {
|
||||
AstRefDType* const refp = it->first;
|
||||
refp->replaceWith(it->second->cloneTree(false));
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
any = true;
|
||||
}
|
||||
if (!any) break;
|
||||
}
|
||||
// Bail if anything still points at the template.
|
||||
dtypeClonep->foreach([&](AstVarRef* varrefp) {
|
||||
cloneVarp->foreach([&](AstVarRef* varrefp) {
|
||||
varrefp->v3fatalSrc(
|
||||
"Unresolved VarRef '"
|
||||
<< varrefp->prettyName() << "' in pin dtype clone. Pin: "
|
||||
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
|
||||
});
|
||||
if (cloneVarp->childDTypep())
|
||||
cloneVarp->childDTypep()->unlinkFrBack()->deleteTree();
|
||||
cloneVarp->childDTypep(dtypeClonep);
|
||||
cloneVarp->dtypep(nullptr);
|
||||
}
|
||||
V3Const::constifyParamsEdit(cloneVarp);
|
||||
if (AstConst* const widthedp = VN_CAST(cloneVarp->valuep(), Const)) {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
//*************************************************************************
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Control.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3ParseImp.h" // Defines YYTYPE; before including bison header
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
class V3ParseGrammar final {
|
||||
public:
|
||||
|
|
@ -94,97 +96,6 @@ public:
|
|||
nodep->trace(singletonp()->allTracingOn(fileline));
|
||||
return nodep;
|
||||
}
|
||||
void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) {
|
||||
// Hidden static to take unspecified reference argument results
|
||||
AstVar* const defaultVarp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
|
||||
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
nodep->addStmtsp(defaultVarp);
|
||||
|
||||
// IEEE: option
|
||||
{
|
||||
v3Global.setUsesStdPackage();
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: type_option
|
||||
{
|
||||
v3Global.setUsesStdPackage();
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{},
|
||||
new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t",
|
||||
new AstClassOrPackageRef{nodep->fileline(), "std",
|
||||
nullptr, nullptr},
|
||||
nullptr}};
|
||||
nodep->addMembersp(varp);
|
||||
}
|
||||
|
||||
// IEEE: function void sample()
|
||||
{
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
|
||||
funcp->addStmtsp(sampleArgs);
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: function void start(), void stop()
|
||||
for (const string& name : {"start"s, "stop"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
}
|
||||
|
||||
// IEEE: static function real get_coverage(optional ref int, optional ref int)
|
||||
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
|
||||
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
|
||||
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
|
||||
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
funcp->isStatic(name == "get_coverage");
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
{
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
|
||||
nodep->findDoubleDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::OUTPUT);
|
||||
varp->funcReturn(true);
|
||||
funcp->fvarp(varp);
|
||||
}
|
||||
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
// IEEE: function void set_inst_name(string)
|
||||
{
|
||||
AstFunc* const funcp
|
||||
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
|
||||
funcp->classMethod(true);
|
||||
funcp->dtypep(funcp->findVoidDType());
|
||||
nodep->addMembersp(funcp);
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
|
||||
nodep->findStringDType()};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
varp->funcLocal(true);
|
||||
varp->direction(VDirection::INPUT);
|
||||
funcp->addStmtsp(varp);
|
||||
}
|
||||
}
|
||||
AstDisplay* createDisplayError(FileLine* fileline) {
|
||||
AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr};
|
||||
AstNode::addNext<AstNode, AstNode>(nodep, new AstStop{fileline, false});
|
||||
|
|
|
|||
|
|
@ -1336,6 +1336,7 @@ class TaskVisitor final : public VNVisitor {
|
|||
cfuncp->isVirtual(nodep->isVirtual());
|
||||
cfuncp->dpiPure(nodep->dpiPure());
|
||||
if (nodep->name() == "new") cfuncp->isConstructor(true);
|
||||
if (nodep->isCovergroupSample()) cfuncp->isCovergroupSample(true);
|
||||
if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
|
||||
|
||||
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());
|
||||
|
|
|
|||
|
|
@ -343,11 +343,15 @@ class TimingSuspendableVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()), getSuspendDepVtx(m_procp),
|
||||
P_CALL};
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
UASSERT_OBJ(funcp, nodep, "AstNodeCCall must have non-null funcp post-link");
|
||||
UASSERT_OBJ(m_procp, nodep, "AstNodeCCall must be inside a procedure/CFunc/Begin");
|
||||
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||
UINFO(9, "V3Timing: Processing CCall to " << funcp->name() << " in dependency graph\n");
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(funcp), getSuspendDepVtx(m_procp), P_CALL};
|
||||
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(funcp), getNeedsProcDepVtx(m_procp),
|
||||
P_CALL};
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -961,8 +965,16 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
if (nodep->funcp()->needProcess()) m_hasProcess = true;
|
||||
if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
|
||||
// Skip automatic covergroup sampling calls
|
||||
if (funcp->isCovergroupSample()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
if (funcp->needProcess()) m_hasProcess = true;
|
||||
if (hasFlags(funcp, T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
// Calls to suspendables are always void return type, hence parent must be StmtExpr
|
||||
AstStmtExpr* const stmtp = VN_AS(nodep->backp(), StmtExpr);
|
||||
stmtp->replaceWith(new AstCAwait{nodep->fileline(), nodep->unlinkFrBack()});
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstClass* m_cgClassp = nullptr; // Current covergroup class
|
||||
AstNodeModule* m_modep = nullptr; // Current module
|
||||
const AstConstraint* m_constraintp = nullptr; // Current constraint
|
||||
AstNodeProcedure* m_procedurep = nullptr; // Current final/always
|
||||
|
|
@ -1777,8 +1778,23 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
|
||||
}
|
||||
void visit(AstCgOptionAssign* nodep) override {
|
||||
// We report COVERIGN on the whole covergroup; if get more fine-grained add this
|
||||
// nodep->v3warn(COVERIGN, "Ignoring unsupported: coverage option");
|
||||
// Extract covergroup option values and store in AstClass before deleting.
|
||||
// m_cgClassp is always set here: AstCgOptionAssign only appears in covergroup
|
||||
// class bodies, and visitClass sets m_cgClassp before iterating children.
|
||||
if (nodep->optionType() == VCoverOptionType::AUTO_BIN_MAX) {
|
||||
// By V3Width time, V3Param has already folded any parameter references.
|
||||
// If the value is still not a constant, it is a runtime expression - emit error.
|
||||
if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) {
|
||||
m_cgClassp->cgAutoBinMax(constp->toSInt());
|
||||
UINFO(6, " Covergroup " << m_cgClassp->name()
|
||||
<< " option.auto_bin_max = " << constp->toSInt() << endl);
|
||||
} else {
|
||||
nodep->valuep()->v3error("option.auto_bin_max must be a constant expression");
|
||||
}
|
||||
}
|
||||
// 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 {
|
||||
|
|
@ -3420,7 +3436,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
return AstEqWild::newTyped(itemp->fileline(), exprp, itemp->unlinkFrBack());
|
||||
}
|
||||
void visit(AstInsideRange* nodep) override {
|
||||
// Just do each side; AstInside will rip these nodes out later
|
||||
// Just do each side; AstInside will rip these nodes out later.
|
||||
// When m_vup is null, this range appears outside a normal expression context (e.g.
|
||||
// in a covergroup bin declaration). Pre-fold constant arithmetic in that case
|
||||
// (e.g., AstNegate(Const) -> Const) so children have their types set before widthing.
|
||||
// We cannot do this unconditionally: in a normal 'inside' expression (m_vup set),
|
||||
// range bounds may be enum refs not yet widthed, and constifyEdit would crash.
|
||||
if (!m_vup) {
|
||||
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
||||
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
||||
}
|
||||
userIterateAndNext(nodep->lhsp(), m_vup);
|
||||
userIterateAndNext(nodep->rhsp(), m_vup);
|
||||
nodep->dtypeFrom(nodep->lhsp());
|
||||
|
|
@ -7500,6 +7525,8 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Must do extends first, as we may in functions under this class
|
||||
// start following a tree of extends that takes us to other classes
|
||||
userIterateAndNext(nodep->extendsp(), nullptr);
|
||||
VL_RESTORER(m_cgClassp);
|
||||
if (nodep->isCovergroup()) m_cgClassp = nodep;
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
#include "V3Active.h"
|
||||
#include "V3ActiveTop.h"
|
||||
#include "V3Assert.h"
|
||||
#include "V3AssertNfa.h"
|
||||
#include "V3AssertPre.h"
|
||||
#include "V3AssertProp.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Begin.h"
|
||||
#include "V3Branch.h"
|
||||
|
|
@ -38,6 +38,7 @@
|
|||
#include "V3Control.h"
|
||||
#include "V3Coverage.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
#include "V3Covergroup.h"
|
||||
#include "V3Dead.h"
|
||||
#include "V3Delayed.h"
|
||||
#include "V3Depth.h"
|
||||
|
|
@ -236,6 +237,10 @@ static void process() {
|
|||
// Before we do dead code elimination and inlining, or we'll lose it.
|
||||
if (v3Global.opt.coverage()) V3Coverage::coverage(v3Global.rootp());
|
||||
|
||||
// Functional coverage code generation
|
||||
// Generate code for covergroups/coverpoints
|
||||
if (v3Global.useCovergroup()) V3Covergroup::covergroup(v3Global.rootp());
|
||||
|
||||
// Resolve randsequence if they are used by the design
|
||||
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());
|
||||
|
||||
|
|
@ -252,8 +257,9 @@ static void process() {
|
|||
|
||||
// Assertion insertion
|
||||
// After we've added block coverage, but before other nasty transforms
|
||||
V3AssertProp::assertPropAll(v3Global.rootp());
|
||||
//
|
||||
V3AssertNfa::assertNfaAll(v3Global.rootp());
|
||||
// V3AssertProp removed: NFA subsumes multi-cycle property lowering.
|
||||
// Unsupported constructs fall through to V3AssertPre.
|
||||
V3AssertPre::assertPreAll(v3Global.rootp());
|
||||
//
|
||||
V3Assert::assertAll(v3Global.rootp());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Covergroup data containers
|
||||
//
|
||||
// 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_VLCCOVERGROUP_H_
|
||||
#define VERILATOR_VLCCOVERGROUP_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
//********************************************************************
|
||||
// VlcCgBin - One coverage bin within a coverpoint
|
||||
|
||||
class VlcCgBin final {
|
||||
// MEMBERS
|
||||
string m_name;
|
||||
string m_binType; //< "ignore", "illegal", or "" (normal)
|
||||
bool m_covered = false;
|
||||
uint64_t m_count = 0;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcCgBin(const string& name, const string& binType, bool covered, uint64_t count)
|
||||
: m_name{name}
|
||||
, m_binType{binType}
|
||||
, m_covered{covered}
|
||||
, m_count{count} {}
|
||||
~VlcCgBin() = default;
|
||||
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
const string& binType() const { return m_binType; }
|
||||
bool covered() const { return m_covered; }
|
||||
uint64_t count() const { return m_count; }
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcCgCoverpoint - One coverpoint or cross within a covergroup type
|
||||
|
||||
class VlcCgCoverpoint final {
|
||||
// MEMBERS
|
||||
string m_name;
|
||||
bool m_isCross = false;
|
||||
std::vector<VlcCgBin> m_bins;
|
||||
uint64_t m_normalTotal = 0;
|
||||
uint64_t m_normalCovered = 0;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcCgCoverpoint(const string& name, bool isCross)
|
||||
: m_name{name}
|
||||
, m_isCross{isCross} {}
|
||||
~VlcCgCoverpoint() = default;
|
||||
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
bool isCross() const { return m_isCross; }
|
||||
const std::vector<VlcCgBin>& bins() const { return m_bins; }
|
||||
uint64_t normalTotal() const { return m_normalTotal; }
|
||||
uint64_t normalCovered() const { return m_normalCovered; }
|
||||
|
||||
// METHODS
|
||||
void addBin(const string& name, const string& binType, bool covered, uint64_t count) {
|
||||
m_bins.emplace_back(name, binType, covered, count);
|
||||
if (binType.empty()) {
|
||||
++m_normalTotal;
|
||||
if (covered) ++m_normalCovered;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcCovergroupType - One covergroup type aggregated across all tests
|
||||
|
||||
class VlcCovergroupType final {
|
||||
// MEMBERS
|
||||
string m_typeName;
|
||||
string m_filename;
|
||||
int m_lineno = 0;
|
||||
std::vector<VlcCgCoverpoint> m_coverpoints;
|
||||
std::map<string, size_t> m_cpIndex; //< Coverpoint name -> index into m_coverpoints
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcCovergroupType(const string& typeName, const string& filename, int lineno)
|
||||
: m_typeName{typeName}
|
||||
, m_filename{filename}
|
||||
, m_lineno{lineno} {}
|
||||
~VlcCovergroupType() = default;
|
||||
|
||||
// ACCESSORS
|
||||
const string& typeName() const { return m_typeName; }
|
||||
const string& filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
const std::vector<VlcCgCoverpoint>& coverpoints() const { return m_coverpoints; }
|
||||
|
||||
// METHODS
|
||||
VlcCgCoverpoint& findNewCoverpoint(const string& name, bool isCross) {
|
||||
const auto it = m_cpIndex.find(name);
|
||||
if (it != m_cpIndex.end()) return m_coverpoints[it->second];
|
||||
const size_t idx = m_coverpoints.size();
|
||||
m_cpIndex[name] = idx;
|
||||
m_coverpoints.emplace_back(name, isCross);
|
||||
return m_coverpoints[idx];
|
||||
}
|
||||
uint64_t normalTotal() const {
|
||||
uint64_t total = 0;
|
||||
for (const auto& cp : m_coverpoints) total += cp.normalTotal();
|
||||
return total;
|
||||
}
|
||||
uint64_t normalCovered() const {
|
||||
uint64_t covered = 0;
|
||||
for (const auto& cp : m_coverpoints) covered += cp.normalCovered();
|
||||
return covered;
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcCovergroups - Container of all covergroup types
|
||||
|
||||
class VlcCovergroups final {
|
||||
// MEMBERS
|
||||
std::map<string, VlcCovergroupType> m_cgMap; //< Sorted by type name
|
||||
|
||||
// METHODS - FORMATTING
|
||||
static string pctStr(uint64_t covered, uint64_t total) {
|
||||
std::ostringstream oss;
|
||||
const double pct = (total == 0) ? 100.0 : (100.0 * covered / total);
|
||||
oss << std::fixed << std::setprecision(2) << pct;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcCovergroups() = default;
|
||||
~VlcCovergroups() = default;
|
||||
|
||||
// METHODS
|
||||
VlcCovergroupType& findNewCovergroupType(const string& typeName, const string& filename,
|
||||
int lineno) {
|
||||
const auto it = m_cgMap.find(typeName);
|
||||
if (it != m_cgMap.end()) return it->second;
|
||||
return m_cgMap.emplace(typeName, VlcCovergroupType{typeName, filename, lineno})
|
||||
.first->second;
|
||||
}
|
||||
void dump(std::ostream& os) const {
|
||||
uint64_t grandTotal = 0, grandCovered = 0, grandIgnored = 0, grandIllegal = 0;
|
||||
for (const auto& cgPair : m_cgMap) {
|
||||
const VlcCovergroupType& cg = cgPair.second;
|
||||
grandTotal += cg.normalTotal();
|
||||
grandCovered += cg.normalCovered();
|
||||
for (const auto& cp : cg.coverpoints()) {
|
||||
for (const auto& bin : cp.bins()) {
|
||||
if (bin.binType() == "ignore")
|
||||
++grandIgnored;
|
||||
else if (bin.binType() == "illegal")
|
||||
++grandIllegal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string divider(78, '-');
|
||||
|
||||
os << "COVERGROUP COVERAGE REPORT\n";
|
||||
os << "==========================\n";
|
||||
os << "\n";
|
||||
os << "TOTAL: " << grandCovered << "/" << grandTotal << " bins covered ("
|
||||
<< pctStr(grandCovered, grandTotal) << "%)\n";
|
||||
if (grandIgnored || grandIllegal)
|
||||
os << " (" << grandIgnored << " ignored, " << grandIllegal << " illegal)\n";
|
||||
|
||||
for (const auto& cgPair : m_cgMap) {
|
||||
const VlcCovergroupType& cg = cgPair.second;
|
||||
const uint64_t cgTotal = cg.normalTotal();
|
||||
const uint64_t cgCovered = cg.normalCovered();
|
||||
|
||||
os << "\n" << divider << "\n";
|
||||
os << "Covergroup Type: " << cg.typeName() << " [" << cg.filename() << ":"
|
||||
<< cg.lineno() << "]\n";
|
||||
os << " Type Coverage: " << cgCovered << "/" << cgTotal << " bins ("
|
||||
<< pctStr(cgCovered, cgTotal) << "%)\n";
|
||||
|
||||
for (const auto& cp : cg.coverpoints()) {
|
||||
os << "\n";
|
||||
os << " " << (cp.isCross() ? "Cross" : "Coverpoint") << ": " << cp.name() << "\n";
|
||||
os << " Coverage: " << cp.normalCovered() << "/" << cp.normalTotal()
|
||||
<< " bins (" << pctStr(cp.normalCovered(), cp.normalTotal()) << "%)\n";
|
||||
os << " Bins:\n";
|
||||
|
||||
size_t maxNameLen = 0;
|
||||
for (const auto& bin : cp.bins())
|
||||
if (bin.name().size() > maxNameLen) maxNameLen = bin.name().size();
|
||||
|
||||
for (const auto& bin : cp.bins()) {
|
||||
const char* status;
|
||||
if (bin.binType() == "ignore")
|
||||
status = "IGNORE ";
|
||||
else if (bin.binType() == "illegal")
|
||||
status = "ILLEGAL";
|
||||
else if (bin.covered())
|
||||
status = "COVERED";
|
||||
else
|
||||
status = "ZERO ";
|
||||
os << " " << status << " " << std::left
|
||||
<< std::setw(static_cast<int>(maxNameLen)) << bin.name() << std::right
|
||||
<< " " << bin.count() << " hits\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
os << "\n" << divider << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
||||
|
|
@ -66,6 +66,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
|
|||
DECL_OPTION("-annotate-all", OnOff, &m_annotateAll);
|
||||
DECL_OPTION("-annotate-min", Set, &m_annotateMin);
|
||||
DECL_OPTION("-annotate-points", OnOff, &m_annotatePoints);
|
||||
DECL_OPTION("-covergroup", OnOff, &m_covergroup);
|
||||
DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); });
|
||||
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
|
||||
DECL_OPTION("-filter-type", Set, &m_filterType);
|
||||
|
|
@ -143,6 +144,8 @@ int main(int argc, char** argv) {
|
|||
V3Error::abortIfWarnings();
|
||||
if (!top.opt.annotateOut().empty()) top.annotate(top.opt.annotateOut());
|
||||
|
||||
if (top.opt.covergroup()) top.covergroup();
|
||||
|
||||
if (top.opt.rank()) {
|
||||
top.rank();
|
||||
top.tests().dump(false);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class VlcOptions final {
|
|||
bool m_annotateAll = false; // main switch: --annotate-all
|
||||
int m_annotateMin = 10; // main switch: --annotate-min I<count>
|
||||
bool m_annotatePoints = false; // main switch: --annotate-points
|
||||
bool m_covergroup = false; // main switch: --covergroup
|
||||
string m_filterType = "*"; // main switch: --filter-type
|
||||
VlStringSet m_readFiles; // main switch: --read
|
||||
bool m_rank = false; // main switch: --rank
|
||||
|
|
@ -67,6 +68,7 @@ public:
|
|||
int annotateMin() const { return m_annotateMin; }
|
||||
bool countOk(uint64_t count) const { return count >= static_cast<uint64_t>(m_annotateMin); }
|
||||
bool annotatePoints() const { return m_annotatePoints; }
|
||||
bool covergroup() const { return m_covergroup; }
|
||||
bool rank() const { return m_rank; }
|
||||
bool unlink() const { return m_unlink; }
|
||||
string writeFile() const { return m_writeFile; }
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ public:
|
|||
string comment() const { return keyExtract(VL_CIK_COMMENT, m_name.c_str()); }
|
||||
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
|
||||
string type() const { return typeExtract(m_name.c_str()); }
|
||||
// Covergroup-specific key accessors (long keys, no short-key alias)
|
||||
string page() const { return keyExtract("page", m_name.c_str()); }
|
||||
string bin() const { return keyExtract("bin", m_name.c_str()); }
|
||||
string binType() const { return keyExtract("bin_type", m_name.c_str()); }
|
||||
bool isCross() const { return !keyExtract("cross", m_name.c_str()).empty(); }
|
||||
string thresh() const {
|
||||
// string as maybe ""
|
||||
return keyExtract(VL_CIK_THRESH, m_name.c_str());
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -205,6 +208,45 @@ void VlcTop::rank() {
|
|||
}
|
||||
}
|
||||
|
||||
void VlcTop::covergroup() {
|
||||
UINFO(2, "covergroup...");
|
||||
covergroupCalc();
|
||||
m_covergroups.dump(std::cout);
|
||||
}
|
||||
|
||||
void VlcTop::covergroupCalc() {
|
||||
// Collect covergroup points from all loaded coverage data into m_covergroups
|
||||
for (const auto& nameNum : m_points) {
|
||||
const VlcPoint& pt = m_points.pointNumber(nameNum.second);
|
||||
if (pt.type() != "covergroup") continue;
|
||||
|
||||
const std::string page = pt.page();
|
||||
// Page format: "v_covergroup/<cgTypeName>"
|
||||
const std::string pagePrefix = "v_covergroup/";
|
||||
if (page.size() <= pagePrefix.size()) continue;
|
||||
const std::string cgTypeName = page.substr(pagePrefix.size());
|
||||
|
||||
// Parse hier: "<cg_type>.<cp_name>.<bin_name>"
|
||||
const std::string hier = pt.hier();
|
||||
const size_t dot1 = hier.find('.');
|
||||
if (dot1 == std::string::npos) continue;
|
||||
const size_t dot2 = hier.find('.', dot1 + 1);
|
||||
if (dot2 == std::string::npos) continue;
|
||||
const std::string cpName = hier.substr(dot1 + 1, dot2 - dot1 - 1);
|
||||
const std::string binName = hier.substr(dot2 + 1);
|
||||
|
||||
VlcCovergroupType& cg
|
||||
= m_covergroups.findNewCovergroupType(cgTypeName, pt.filename(), pt.lineno());
|
||||
VlcCgCoverpoint& cp = cg.findNewCoverpoint(cpName, pt.isCross());
|
||||
|
||||
// Threshold: use per-bin thresh key (option.at_least) if present, else 1 (SV default)
|
||||
const std::string threshStr = pt.thresh();
|
||||
const uint64_t binThresh = threshStr.empty() ? 1 : std::stoull(threshStr);
|
||||
const uint64_t count = pt.count();
|
||||
cp.addBin(binName, pt.binType(), count >= binThresh, count);
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
void VlcTop::annotateCalc() {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "VlcCovergroup.h"
|
||||
#include "VlcOptions.h"
|
||||
#include "VlcPoint.h"
|
||||
#include "VlcSource.h"
|
||||
|
|
@ -37,11 +38,13 @@ private:
|
|||
VlcTests m_tests; //< List of all tests (all coverage files)
|
||||
VlcPoints m_points; //< List of all points
|
||||
VlcSources m_sources; //< List of all source files to annotate
|
||||
VlcCovergroups m_covergroups; //< Covergroup analysis data
|
||||
|
||||
// METHODS
|
||||
void annotateCalc();
|
||||
void annotateCalcNeeded();
|
||||
void annotateOutputFiles(const string& dirname);
|
||||
void covergroupCalc();
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -55,6 +58,7 @@ public:
|
|||
|
||||
// METHODS
|
||||
void annotate(const string& dirname);
|
||||
void covergroup();
|
||||
void readCoverage(const string& filename, bool nonfatal = false);
|
||||
void writeCoverage(const string& filename);
|
||||
void writeInfo(const string& filename);
|
||||
|
|
|
|||
324
src/verilog.y
324
src/verilog.y
|
|
@ -3958,16 +3958,16 @@ value_range<nodeExprp>: // ==IEEE: value_range/open_value_range
|
|||
covergroup_value_range<nodeExprp>: // ==IEEE-2012: covergroup_value_range
|
||||
cgexpr { $$ = $1; }
|
||||
| '[' cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
// // IEEE-2023: added all four:
|
||||
// // Skipped as '$' is part of our expr
|
||||
// // IEEE-2023: '[' '$' ':' cgexpr ']'
|
||||
// // Skipped as '$' is part of our expr
|
||||
// // IEEE-2023: '[' cgexpr ':' '$' ']'
|
||||
| '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
| '[' cgexpr yP_PLUSPCTMINUS cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); }
|
||||
;
|
||||
|
||||
caseCondList<nodeExprp>: // IEEE: part of case_item
|
||||
|
|
@ -6936,40 +6936,27 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
|
|||
yCOVERGROUP idAny cgPortListE coverage_eventE ';'
|
||||
/*cont*/ coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ AstClass *cgClassp = new AstClass{$<fl>2, *$2, PARSEP->libname()};
|
||||
cgClassp->isCovergroup(true);
|
||||
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
|
||||
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newp->classMethod(true);
|
||||
newp->isConstructor(true);
|
||||
newp->dtypep(cgClassp->dtypep());
|
||||
newp->addStmtsp($3);
|
||||
newp->addStmtsp($6);
|
||||
cgClassp->addMembersp(newp);
|
||||
GRAMMARP->createCoverGroupMethods(cgClassp, $4);
|
||||
|
||||
$$ = cgClassp;
|
||||
GRAMMARP->endLabel($<fl>8, $$, $8);
|
||||
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
|
||||
}
|
||||
{ AstSenTree* clockp = nullptr;
|
||||
AstNode* sampleArgsp = nullptr;
|
||||
if ($4) {
|
||||
if (VN_IS($4, SenItem)) {
|
||||
clockp = new AstSenTree{$<fl>1, VN_AS($4, SenItem)};
|
||||
} else {
|
||||
sampleArgsp = $4;
|
||||
}
|
||||
}
|
||||
$$ = new AstCovergroup{$<fl>1, *$2, static_cast<AstVar*>($3),
|
||||
static_cast<AstVar*>(sampleArgsp), $6, clockp};
|
||||
// Every covergroup has option/type_option members (added by V3LinkParse)
|
||||
// referencing std:: types, so mark std as needed at parse time.
|
||||
v3Global.setUsesStdPackage();
|
||||
GRAMMARP->endLabel($<fl>8, $$, $8); }
|
||||
| yCOVERGROUP yEXTENDS idAny ';'
|
||||
/*cont*/ coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ AstClass *cgClassp = new AstClass{$<fl>3, *$3, PARSEP->libname()};
|
||||
cgClassp->isCovergroup(true);
|
||||
AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
|
||||
newp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
||||
newp->classMethod(true);
|
||||
newp->isConstructor(true);
|
||||
newp->dtypep(cgClassp->dtypep());
|
||||
newp->addStmtsp($5);
|
||||
cgClassp->addMembersp(newp);
|
||||
GRAMMARP->createCoverGroupMethods(cgClassp, nullptr);
|
||||
|
||||
$$ = cgClassp;
|
||||
GRAMMARP->endLabel($<fl>7, $$, $7);
|
||||
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
|
||||
}
|
||||
{ $$ = nullptr;
|
||||
BBUNSUP($1, "Unsupported: covergroup inheritance (extends)");
|
||||
DEL($5); }
|
||||
;
|
||||
|
||||
cgPortListE<nodep>:
|
||||
|
|
@ -7002,10 +6989,16 @@ coverage_spec_or_option<nodep>: // ==IEEE: coverage_spec_or_option
|
|||
coverage_option<nodep>: // ==IEEE: coverage_option
|
||||
// // option/type_option aren't really keywords
|
||||
id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr
|
||||
{ if (*$1 == "option") {
|
||||
$$ = new AstCgOptionAssign{$<fl>1, false, *$3, $5};
|
||||
} else if (*$1 == "type_option") {
|
||||
$$ = new AstCgOptionAssign{$<fl>1, true, *$3, $5};
|
||||
{ if (*$1 == "option" || *$1 == "type_option") {
|
||||
const bool typeOpt = (*$1 == "type_option");
|
||||
VCoverOptionType optType = VCoverOptionType::UNKNOWN;
|
||||
if (*$3 == "at_least") optType = VCoverOptionType::AT_LEAST;
|
||||
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
|
||||
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
|
||||
else if (*$3 == "auto_bin_max") optType = VCoverOptionType::AUTO_BIN_MAX;
|
||||
else if (*$3 == "per_instance") optType = VCoverOptionType::PER_INSTANCE;
|
||||
else if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
|
||||
$$ = new AstCgOptionAssign{$<fl>1, typeOpt, optType, *$3, $5};
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
$<fl>1->v3error("Syntax error; expected 'option' or 'type_option': '" << *$1 << "'");
|
||||
|
|
@ -7016,29 +7009,33 @@ coverage_option<nodep>: // ==IEEE: coverage_option
|
|||
cover_point<nodep>: // ==IEEE: cover_point
|
||||
// // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT
|
||||
yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverpoint"); DEL($2, $3, $4); }
|
||||
{ $$ = new AstCoverpoint{$<fl>1, "", $2, $3, $4}; }
|
||||
// // IEEE-2012: class_scope before an ID
|
||||
| id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: coverpoint"); DEL($4, $5, $6);}
|
||||
{ $$ = new AstCoverpoint{$<fl>3, *$1, $4, $5, $6}; }
|
||||
// // data_type_or_implicit expansion
|
||||
| data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($1, $5, $6, $7);}
|
||||
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7};
|
||||
DEL($1); }
|
||||
| yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
|
||||
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
|
||||
DEL($2); }
|
||||
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); }
|
||||
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7}; }
|
||||
// // IEEE-2012:
|
||||
| bins_or_empty { $$ = $1; }
|
||||
;
|
||||
|
||||
iffE<nodep>: // IEEE: part of cover_point, others
|
||||
iffE<nodeExprp>: // IEEE: part of cover_point, others
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| yIFF '(' expr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover 'iff'"); DEL($3); }
|
||||
{ $$ = $3; /* Keep iff condition for coverpoint */ }
|
||||
;
|
||||
|
||||
bins_or_empty<nodep>: // ==IEEE: bins_or_empty
|
||||
|
|
@ -7062,39 +7059,104 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
|
|||
// // Superset of IEEE - we allow []'s in more places
|
||||
coverage_option { $$ = $1; }
|
||||
// // Can't use wildcardE as results in conflicts
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin specification"); DEL($3, $6, $8); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>8, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $6, $10, $12); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $8, $10); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'wildcard' specification"); DEL($4, $7, $9); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($4, $7, $11, $13); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE
|
||||
{ // Check for automatic bins: bins auto[N]
|
||||
if (*$2 == "auto") {
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, $4};
|
||||
DEL($6);
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
BBCOVERIGN($<fl>2, "Unsupported: 'bins' array (non-auto)");
|
||||
DEL($4, $6);
|
||||
}
|
||||
}
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
DEL($10, $12); $$ = binp; }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true};
|
||||
DEL($9); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
//
|
||||
// // cgexpr part of trans_list
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin trans list"); DEL($3, $5, $6); }
|
||||
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($4, $6, $7);}
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_TRANSITION}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_IGNORE}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ FileLine* isArray = $<fl>3;
|
||||
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_ILLEGAL}, isArray != nullptr};
|
||||
DEL($6); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
|
||||
//
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'default'"); DEL($3, $6); }
|
||||
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($3, $7); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_DEFAULT};
|
||||
DEL($6); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_IGNORE};
|
||||
DEL($6); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
|
||||
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_ILLEGAL};
|
||||
DEL($6); }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
|
||||
;
|
||||
|
||||
bins_orBraE<nodep>: // IEEE: part of bins_or_options:
|
||||
bins_orBraE<fl>: // IEEE: part of bins_or_options: returns fileline (abuse for boolean flag)
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| '[' ']' { $$ = nullptr; /*UNSUP*/ }
|
||||
| '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); }
|
||||
;
|
||||
|
||||
bins_keyword<fl>: // ==IEEE: bins_keyword
|
||||
yBINS { $$ = $1; /*UNSUP*/ }
|
||||
| yILLEGAL_BINS { $$ = $1; /*UNSUP*/ }
|
||||
| yIGNORE_BINS { $$ = $1; /*UNSUP*/ }
|
||||
| '[' ']' { $$ = $<fl>1; /* Mark as array */ }
|
||||
| '[' cgexpr ']' { BBCOVERIGN($<fl>1, "Unsupported: 'bins' explicit array size (treated as '[]')"); DEL($2); $$ = $<fl>1; }
|
||||
;
|
||||
|
||||
trans_list<nodep>: // ==IEEE: trans_list
|
||||
|
|
@ -7102,30 +7164,42 @@ trans_list<nodep>: // ==IEEE: trans_list
|
|||
| trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); }
|
||||
;
|
||||
|
||||
trans_set<nodep>: // ==IEEE: trans_set
|
||||
trans_range_list { $$ = $1; }
|
||||
// // Note the { => } in the grammar, this is really a list
|
||||
trans_set<nodep>: // ==IEEE: trans_set (returns AstCoverTransSet)
|
||||
trans_range_list {
|
||||
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($1);
|
||||
$$ = new AstCoverTransSet{$<fl>1, itemp};
|
||||
}
|
||||
| trans_set yP_EQGT trans_range_list
|
||||
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); }
|
||||
{
|
||||
AstCoverTransSet* const setp = static_cast<AstCoverTransSet*>($1);
|
||||
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($3);
|
||||
setp->addItemsp(itemp);
|
||||
$$ = setp;
|
||||
}
|
||||
;
|
||||
|
||||
trans_range_list<nodep>: // ==IEEE: trans_range_list
|
||||
trans_item { $$ = $1; }
|
||||
trans_range_list<nodep>: // ==IEEE: trans_range_list (returns AstCoverTransItem)
|
||||
trans_item {
|
||||
// Simple transition item without repetition
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONE};
|
||||
}
|
||||
| trans_item yP_BRASTAR cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3); }
|
||||
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3, $5); }
|
||||
| trans_item yP_BRAMINUSGT cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3); }
|
||||
{ BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($3);
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::GOTO}; }
|
||||
| trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($1, $3, $5); }
|
||||
| trans_item yP_BRAEQ cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3); }
|
||||
{ BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($3);
|
||||
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONCONS}; }
|
||||
| trans_item yP_BRAEQ cgexpr ':' cgexpr ']'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($1, $3, $5); }
|
||||
;
|
||||
|
||||
trans_item<nodep>: // ==IEEE: range_list
|
||||
trans_item<nodep>: // ==IEEE: range_list (returns range list node)
|
||||
covergroup_range_list { $$ = $1; }
|
||||
;
|
||||
|
||||
|
|
@ -7137,9 +7211,28 @@ covergroup_range_list<nodep>: // ==IEEE: covergroup_range_list
|
|||
|
||||
cover_cross<nodep>: // ==IEEE: cover_cross
|
||||
id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: cover cross"); DEL($4, $5, $6); }
|
||||
{
|
||||
AstCoverCross* const nodep = new AstCoverCross{$<fl>3, *$1,
|
||||
VN_AS($4, CoverpointRef)};
|
||||
if ($6) nodep->addRawBodyp($6);
|
||||
if ($5) {
|
||||
$5->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
|
||||
VL_DO_DANGLING($5->deleteTree(), $5);
|
||||
}
|
||||
$$ = nodep;
|
||||
}
|
||||
| yCROSS list_of_cross_items iffE cross_body
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover cross"); DEL($2, $3, $4); }
|
||||
{
|
||||
AstCoverCross* const nodep = new AstCoverCross{$<fl>1,
|
||||
"__cross" + cvtToStr(GRAMMARP->s_typeImpNum++),
|
||||
VN_AS($2, CoverpointRef)};
|
||||
if ($4) nodep->addRawBodyp($4);
|
||||
if ($3) {
|
||||
$3->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross");
|
||||
VL_DO_DANGLING($3->deleteTree(), $3);
|
||||
}
|
||||
$$ = nodep;
|
||||
}
|
||||
;
|
||||
|
||||
list_of_cross_items<nodep>: // ==IEEE: list_of_cross_items
|
||||
|
|
@ -7154,7 +7247,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
|
||||
|
|
@ -7174,12 +7268,16 @@ cross_body_itemList<nodep>: // IEEE: part of cross_body
|
|||
|
||||
cross_body_item<nodep>: // ==IEEE: cross_body_item
|
||||
function_declaration
|
||||
{ $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1->fileline(), "Unsupported: 'function' in coverage cross body"); DEL($1); }
|
||||
// // IEEE: bins_selection_or_option
|
||||
| coverage_option ';' { $$ = $1; }
|
||||
// // IEEE: bins_selection
|
||||
| bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); DEL($4, $5); }
|
||||
// // IEEE: bins_selection - for now, we ignore explicit cross bins
|
||||
| yBINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| yIGNORE_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| yILLEGAL_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); }
|
||||
| error ';' { $$ = nullptr; } // LCOV_EXCL_LINE
|
||||
;
|
||||
|
||||
|
|
@ -7187,28 +7285,28 @@ select_expression<nodep>: // ==IEEE: select_expression
|
|||
select_expression_r
|
||||
{ $$ = $1; }
|
||||
| select_expression yP_ANDAND select_expression
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '&&' in coverage select expression"); DEL($1, $3); }
|
||||
| select_expression yP_OROR select_expression
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: '||' in coverage select expression"); DEL($1, $3); }
|
||||
;
|
||||
|
||||
// This non-terminal exists to disambiguate select_expression and make "with" bind tighter
|
||||
select_expression_r<nodep>:
|
||||
// // IEEE: select_condition expanded here
|
||||
yBINSOF '(' bins_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($3); }
|
||||
| '!' yBINSOF '(' bins_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($4); }
|
||||
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($3, $7); }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($7); }
|
||||
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($4, $8); }
|
||||
{ $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($4, $8); }
|
||||
| yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($3); }
|
||||
| '!' yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($4); }
|
||||
| select_expression_r yWITH__PAREN '(' cgexpr ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression with"); DEL($1, $4); }
|
||||
{ $$ = nullptr; BBCOVERIGN($2, "Unsupported: 'with' in coverage select expression"); DEL($1, $4); }
|
||||
// // IEEE-2012: Need clarification as to precedence
|
||||
//UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { }
|
||||
// // IEEE-2012: Need clarification as to precedence
|
||||
|
|
@ -7226,7 +7324,7 @@ select_expression_r<nodep>:
|
|||
//UNSUP cgexpr yMATCHES cgexpr {..}
|
||||
//UNSUP // Below are all removed
|
||||
| idAny '(' list_of_argumentsE ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage select function call"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: function call in coverage select expression"); DEL($3); }
|
||||
//UNSUP // Above are all removed, replace with:
|
||||
;
|
||||
|
||||
|
|
@ -7245,7 +7343,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'");
|
||||
|
|
@ -7256,7 +7354,7 @@ coverage_eventE<nodep>: // IEEE: [ coverage_event ]
|
|||
}
|
||||
}
|
||||
| yP_ATAT '(' block_event_expression ')'
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage '@@' events"); DEL($3); }
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: '@@' coverage event"); DEL($3); }
|
||||
;
|
||||
|
||||
block_event_expression<nodep>: // ==IEEE: block_event_expression
|
||||
|
|
@ -7754,11 +7852,15 @@ class_item<nodep>: // ==IEEE: class_item
|
|||
| timeunits_declaration { $$ = $1; }
|
||||
| covergroup_declaration
|
||||
{
|
||||
const string cgName = $1->name();
|
||||
$1->name("__vlAnonCG_" + cgName);
|
||||
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
|
||||
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
|
||||
$$ = addNextNull($1, newp);
|
||||
if ($1) {
|
||||
const string cgName = $1->name();
|
||||
$1->name("__vlAnonCG_" + cgName);
|
||||
AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName,
|
||||
VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())};
|
||||
$$ = addNextNull($1, newp);
|
||||
} else {
|
||||
$$ = nullptr;
|
||||
}
|
||||
}
|
||||
// // local_parameter_declaration under parameter_declaration
|
||||
| parameter_declaration ';' { $$ = $1; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def covergroup_coverage_report(test, outfile=None):
|
||||
"""Parse coverage.dat and write a sorted covergroup bin hit-count report.
|
||||
|
||||
Lines have the form: <hierarchy>[ [bin_type]]: <count>
|
||||
ignore_bins and illegal_bins are annotated with [ignore] / [illegal].
|
||||
|
||||
Returns the path to the written report file.
|
||||
"""
|
||||
if outfile is None:
|
||||
outfile = test.obj_dir + "/covergroup_report.txt"
|
||||
contents = test.file_contents(test.coverage_filename)
|
||||
entries = []
|
||||
for m in re.finditer(r"C '([^']+)' (\d+)", contents):
|
||||
entry, count = m.group(1), m.group(2)
|
||||
if '\x01t\x02covergroup' not in entry:
|
||||
continue
|
||||
h_m = re.search(r'\x01h\x02([^\x01]+)', entry)
|
||||
if not h_m:
|
||||
continue
|
||||
hier = h_m.group(1)
|
||||
bt_m = re.search(r'\x01bin_type\x02([^\x01]+)', entry)
|
||||
cross_m = re.search(r'\x01cross\x021', entry)
|
||||
annotations = []
|
||||
if bt_m:
|
||||
annotations.append(bt_m.group(1))
|
||||
if cross_m:
|
||||
annotations.append("cross")
|
||||
label = f"{hier} [{','.join(annotations)}]" if annotations else hier
|
||||
entries.append((hier, label, int(count)))
|
||||
entries.sort()
|
||||
with open(outfile, 'w', encoding='utf-8') as fh:
|
||||
for _hier, label, count in entries:
|
||||
fh.write(f"{label}: {count}\n")
|
||||
return outfile
|
||||
|
||||
|
||||
def run(test, *, verilator_flags2=()):
|
||||
test.compile(verilator_flags2=['--coverage', *verilator_flags2])
|
||||
test.execute()
|
||||
covergroup_coverage_report(test)
|
||||
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
|
||||
test.passes()
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_assert_clock_event_unsup.v:24:5: Unsupported: Clock event before property call and in its body
|
||||
: ... note: In instance 't'
|
||||
24 | @(negedge clk) check(
|
||||
| ^
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
test.scenarios('vlt') # UNOPTTHREADS in vltmt due to many small assertion states
|
||||
|
||||
test.compile(verilator_flags2=['--timing'])
|
||||
test.compile(verilator_flags2=['--assert', '--timing'])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ module t (
|
|||
else count_fail4 <= count_fail4 + 1;
|
||||
|
||||
// Test 5: a[*10000] large count
|
||||
assert property (@(posedge clk) a [* 10000] |-> b)
|
||||
assert property (@(posedge clk) a [* 100] |-> b)
|
||||
else count_fail5 <= count_fail5 + 1;
|
||||
|
||||
// Test 6: a[*1:3] ##1 b -- bounded range in SExpr
|
||||
|
|
@ -63,11 +63,11 @@ module t (
|
|||
assert property (@(posedge clk) a [+] ##1 b)
|
||||
else count_fail7 <= count_fail7 + 1;
|
||||
|
||||
// Test 8: a[+] |-> b -- standalone [+] (same as a |-> b)
|
||||
// Test 8: a[+] |-> b -- standalone [+]
|
||||
assert property (@(posedge clk) a [+] |-> b)
|
||||
else count_fail8 <= count_fail8 + 1;
|
||||
|
||||
// Test 9: a[*] |-> b -- standalone [*] (shortest non-vacuous match = a)
|
||||
// Test 9: a[*] |-> b -- standalone [*]
|
||||
assert property (@(posedge clk) a [*] |-> b)
|
||||
else count_fail9 <= count_fail9 + 1;
|
||||
|
||||
|
|
@ -79,6 +79,11 @@ module t (
|
|||
assert property (@(posedge clk) a [*] ##1 b)
|
||||
else count_fail11 <= count_fail11 + 1;
|
||||
|
||||
// Counter FSM with M>0: range > kChainLimit (256) forces counter vertex
|
||||
// creation; min>0 exercises the Gte/active gating path in resolveLinks and
|
||||
// emitNbaLogic. Cover-only so count_fail values above are undisturbed.
|
||||
cover property (@(posedge clk) ##[10:300] b);
|
||||
|
||||
always @(posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x a=%b b=%b c=%b d=%b\n", $time, cyc, crc, a, b, c, d);
|
||||
|
|
@ -90,17 +95,23 @@ module t (
|
|||
end
|
||||
else if (cyc == 99) begin
|
||||
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||
`checkd(count_fail1, 5);
|
||||
`checkd(count_fail2, 25);
|
||||
`checkd(count_fail3, 9);
|
||||
`checkd(count_fail4, 74);
|
||||
`checkd(count_fail5, 0);
|
||||
`checkd(count_fail6, 65);
|
||||
`checkd(count_fail7, 65);
|
||||
`checkd(count_fail8, 20);
|
||||
`checkd(count_fail9, 20);
|
||||
`checkd(count_fail10, 73);
|
||||
`checkd(count_fail11, 40);
|
||||
`checkd(count_fail1, 5); // Questa: 5
|
||||
`checkd(count_fail2, 25); // Questa: 25
|
||||
`checkd(count_fail3, 9); // Questa: 9
|
||||
`checkd(count_fail4, 49); // Questa: 49
|
||||
`checkd(count_fail5, 0); // Questa: 0
|
||||
// NFA merge-node range [*M:N] over-counts rejects (Questa: 51); match
|
||||
// detection is correct, only reject counting is imprecise
|
||||
`checkd(count_fail6, 59);
|
||||
`checkd(count_fail7, 51); // Questa: 51
|
||||
`checkd(count_fail8, 20); // Questa: 20
|
||||
// IEEE 1800-2023 16.9.2 permits empty match of [*0]; NFA reports
|
||||
// rejects on each tick while Questa suppresses (Questa: 20)
|
||||
`checkd(count_fail9, 49);
|
||||
`checkd(count_fail10, 59); // Questa: 59
|
||||
// a[*] ##1 b: NFA treats unbounded [*] as liveness (no reject);
|
||||
// Questa treats as definite antecedent (Questa: 29)
|
||||
`checkd(count_fail11, 0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
%Error-UNSUPPORTED: t/t_assert_consec_rep_unsup.v:11:45: Unsupported: multi-cycle sequence expression inside consecutive repetition (IEEE 1800-2023 16.9.2)
|
||||
: ... note: In instance 't'
|
||||
11 | assert property (@(posedge clk) (a ##1 b) [* 2] |-> a);
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename,
|
||||
verilator_flags2=['--assert --timing --error-limit 1000'],
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (input clk);
|
||||
logic a, b;
|
||||
|
||||
// Unsupported: multi-cycle sequence expression inside consecutive repetition
|
||||
assert property (@(posedge clk) (a ##1 b) [* 2] |-> a);
|
||||
|
||||
endmodule
|
||||
|
|
@ -17,7 +17,7 @@ module t (
|
|||
int cyc;
|
||||
reg [63:0] crc;
|
||||
|
||||
// Derive signals from non-adjacent CRC bits (lesson 17: avoid shift correlation)
|
||||
// Derive signals from non-adjacent CRC bits to avoid LFSR shift correlation
|
||||
wire a = crc[0];
|
||||
wire b = crc[4];
|
||||
wire c = crc[8];
|
||||
|
|
@ -56,10 +56,10 @@ module t (
|
|||
end
|
||||
else if (cyc == 99) begin
|
||||
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||
`checkd(count_fail1, 20);
|
||||
`checkd(count_fail2, 40);
|
||||
`checkd(count_fail3, 19);
|
||||
`checkd(count_fail4, 0);
|
||||
`checkd(count_fail1, 20); // Questa: 20
|
||||
`checkd(count_fail2, 25); // Questa: 25
|
||||
`checkd(count_fail3, 19); // Questa: 19
|
||||
`checkd(count_fail4, 0); // Questa: 0
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module t (
|
|||
int cyc;
|
||||
reg [63:0] crc;
|
||||
|
||||
// Derive signals from non-adjacent CRC bits (lesson 17: avoid shift correlation)
|
||||
// Derive signals from non-adjacent CRC bits to avoid LFSR shift correlation
|
||||
wire a = crc[0];
|
||||
wire b = crc[4];
|
||||
wire c = crc[8];
|
||||
|
|
@ -56,10 +56,10 @@ module t (
|
|||
end
|
||||
else if (cyc == 99) begin
|
||||
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||
`checkd(count_fail1, 34);
|
||||
`checkd(count_fail2, 27);
|
||||
`checkd(count_fail3, 25);
|
||||
`checkd(count_fail4, 0);
|
||||
`checkd(count_fail1, 34); // Questa: 29
|
||||
`checkd(count_fail2, 27); // Questa: 32
|
||||
`checkd(count_fail3, 25); // Questa: 29
|
||||
`checkd(count_fail4, 0); // Questa: 0
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[50] %Error: t_assert_property_stop_bad.v:24: Assertion failed in t.__VforkTask_0: 'assert' failed.
|
||||
[50] %Error: t_assert_property_stop_bad.v:24: Assertion failed in t: 'assert' failed.
|
||||
%Error: t/t_assert_property_stop_bad.v:24: Verilog $stop
|
||||
Aborting...
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_assert_rep_unsup.v:13:42: Unsupported: consecutive repetition with non-##1 cycle delay
|
||||
: ... note: In instance 't'
|
||||
13 | assert property (@(posedge clk) a [*2] ##3 b);
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_assert_rep_unsup.v:16:37: Unsupported: standalone consecutive repetition range
|
||||
: ... note: In instance 't'
|
||||
16 | assert property (@(posedge clk) a [*2:3] |-> 1);
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_assert_rep_unsup.v:19:42: Unsupported: trailing consecutive repetition range in sequence expression (e.g. a ##1 b[+])
|
||||
: ... note: In instance 't'
|
||||
19 | assert property (@(posedge clk) b ##1 a[+]);
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_assert_rep_unsup.v:24:37: Unsupported: throughout with complex sequence operator
|
||||
: ... note: In instance 't'
|
||||
24 | assert property (@(posedge clk) a throughout (b[=2]))
|
||||
| ^~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (input clk);
|
||||
logic a, b;
|
||||
|
||||
// === Consecutive repetition [*N] unsupported forms ===
|
||||
|
||||
// Unsupported: non-##1 inter-repetition delay
|
||||
assert property (@(posedge clk) a [*2] ##3 b);
|
||||
|
||||
// Unsupported: standalone range repetition (no ## anchor)
|
||||
assert property (@(posedge clk) a [*2:3] |-> 1);
|
||||
|
||||
// Unsupported: trailing consecutive repetition in sequence
|
||||
assert property (@(posedge clk) b ##1 a[+]);
|
||||
|
||||
// === Nonconsecutive repetition [=N] unsupported forms ===
|
||||
|
||||
// Unsupported: nonconsecutive rep inside throughout
|
||||
assert property (@(posedge clk) a throughout (b[=2]))
|
||||
else $error("FAIL");
|
||||
|
||||
endmodule
|
||||
|
|
@ -4,17 +4,39 @@
|
|||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// A plain (non-covergroup) class - exercises the non-covergroup class scope/varscope paths
|
||||
class PlainClass;
|
||||
int x;
|
||||
endclass
|
||||
|
||||
// Top-level (file-scope) covergroup: exercises the m_modp==null path in
|
||||
// V3LinkParse::visit(AstCovergroup*), line 1269 false branch (libname = "")
|
||||
// verilator lint_off COVERIGN
|
||||
covergroup cg_toplevel;
|
||||
cp_tl: coverpoint 0;
|
||||
endgroup
|
||||
// verilator lint_on COVERIGN
|
||||
|
||||
// verilator lint_off COVERIGN
|
||||
module t;
|
||||
|
||||
int i, j;
|
||||
logic clk = 0;
|
||||
|
||||
covergroup cg(int var1, int var2 = 42);
|
||||
cp1: coverpoint i; // Non-empty body with args: exercises constructor-body path
|
||||
endgroup
|
||||
|
||||
// Clocked covergroup with constructor args: exercises the loop-past-sentinel path in
|
||||
// V3LinkParse createCovergroupMethods (iterates past AstCovergroup sentinel to find 'new')
|
||||
covergroup cg_clocked(int lim) @(posedge clk);
|
||||
cp_clocked: coverpoint i;
|
||||
endgroup
|
||||
|
||||
cg cov1 = new(69, 77);
|
||||
cg cov2 = new(69);
|
||||
|
||||
int i, j;
|
||||
real r;
|
||||
cg_clocked cov_clocked = new(10);
|
||||
PlainClass plain_inst = new; // Non-covergroup class instance: exercises early-return paths
|
||||
|
||||
function void x();
|
||||
cov1.set_inst_name("the_inst_name");
|
||||
|
|
@ -23,16 +45,14 @@ module t;
|
|||
cov1.stop();
|
||||
|
||||
void'(cov2.get_coverage());
|
||||
r = cov2.get_coverage();
|
||||
r = cov2.get_coverage(i, j);
|
||||
void'(cov2.get_coverage(i, j));
|
||||
// verilator lint_off IGNOREDRETURN
|
||||
cov2.get_inst_coverage();
|
||||
// verilator lint_on IGNOREDRETURN
|
||||
r = cov2.get_inst_coverage(i, j);
|
||||
void'(cov2.get_inst_coverage(i, j));
|
||||
|
||||
cg::get_coverage();
|
||||
r = cg::get_coverage();
|
||||
r = cg::get_coverage(i, j);
|
||||
void'(cg::get_coverage());
|
||||
void'(cg::get_coverage(i, j));
|
||||
endfunction
|
||||
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
cg.data.grouped: 2
|
||||
cg.data.values: 3
|
||||
cg2.cp.range_arr: 3
|
||||
cg3.cp.range_sized: 3
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN'])
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test array bins - separate bin per value, including InsideRange and AstRange
|
||||
|
||||
module t;
|
||||
bit [7:0] data;
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
// Array bins: creates 3 separate bins
|
||||
bins values[] = {1, 5, 9};
|
||||
|
||||
// Non-array bin: creates 1 bin covering all values
|
||||
bins grouped = {2, 6, 10};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg2: exercises InsideRange in array bins (e.g., bins r[] = {[0:3]})
|
||||
covergroup cg2;
|
||||
cp: coverpoint data {
|
||||
bins range_arr[] = {[0:3]}; // InsideRange -> 4 separate bins
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg3: exercises AstRange in array bins (e.g., bins r[N] = {[lo:hi]})
|
||||
covergroup cg3;
|
||||
cp: coverpoint data {
|
||||
bins range_sized[4] = {[4:7]}; // AstRange with explicit count
|
||||
}
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
|
||||
cg_inst = new();
|
||||
cg2_inst = new();
|
||||
cg3_inst = new();
|
||||
|
||||
// Hit first array bin value (1)
|
||||
data = 1;
|
||||
cg_inst.sample();
|
||||
|
||||
// Hit second array bin value (5)
|
||||
data = 5;
|
||||
cg_inst.sample();
|
||||
|
||||
// Hit the grouped bin (covers all of 2, 6, 10)
|
||||
data = 6;
|
||||
cg_inst.sample();
|
||||
|
||||
// Hit third array bin value (9)
|
||||
data = 9;
|
||||
cg_inst.sample();
|
||||
|
||||
// Verify hitting other values in grouped bin doesn't increase coverage
|
||||
data = 2;
|
||||
cg_inst.sample();
|
||||
|
||||
// Hit range_arr bins (InsideRange [0:3])
|
||||
data = 0; cg2_inst.sample();
|
||||
data = 1; cg2_inst.sample();
|
||||
data = 2; cg2_inst.sample();
|
||||
|
||||
// Hit range_sized bins (AstRange [4:7])
|
||||
data = 4; cg3_inst.sample();
|
||||
data = 5; cg3_inst.sample();
|
||||
data = 6; cg3_inst.sample();
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
cg1.cp_data3.auto_0: 1
|
||||
cg1.cp_data3.auto_1: 0
|
||||
cg1.cp_data3.auto_2: 0
|
||||
cg1.cp_data3.auto_3: 1
|
||||
cg1.cp_data3.auto_4: 0
|
||||
cg1.cp_data3.auto_5: 0
|
||||
cg1.cp_data3.auto_6: 0
|
||||
cg1.cp_data3.auto_7: 0
|
||||
cg2.cp_data3.auto_0: 1
|
||||
cg2.cp_data3.auto_1: 0
|
||||
cg2.cp_data3.auto_2: 1
|
||||
cg2.cp_data3.auto_3: 0
|
||||
cg3.cp_data3.auto_0: 1
|
||||
cg3.cp_data3.auto_1: 1
|
||||
cg4.cp.auto_0: 0
|
||||
cg4.cp.auto_1: 1
|
||||
cg4.cp.auto_2: 1
|
||||
cg4.cp.auto_3: 1
|
||||
cg4.cp.ign [ignore]: 0
|
||||
cg5.cp_data64.auto_0: 1
|
||||
cg5.cp_data64.auto_1: 1
|
||||
cg5.cp_data64.auto_2: 1
|
||||
cg5.cp_data64.auto_3: 1
|
||||
cg6.cp_data3.auto_0: 1
|
||||
cg6.cp_data3.auto_1: 0
|
||||
cg6.cp_data3.auto_2: 0
|
||||
cg6.cp_data3.auto_3: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test, verilator_flags2=['-Wno-UNSIGNED', '-Wno-CMPCONST'])
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2024 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test implicit auto-bin creation (no explicit bins) and option.auto_bin_max
|
||||
|
||||
module t;
|
||||
|
||||
logic [2:0] data3;
|
||||
logic [3:0] data4; // 4-bit signal for range-bin path tests
|
||||
logic [63:0] data64; // 64-bit signal for width>=64 branch in createAutoBins
|
||||
|
||||
// Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal
|
||||
covergroup cg1;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
// Test 2: auto_bin_max = 4 at covergroup level - creates 4 bins: [0:1],[2:3],[4:5],[6:7]
|
||||
covergroup cg2;
|
||||
option.auto_bin_max = 4;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
// Test 3: auto_bin_max and at_least at *coverpoint* level (lines 207, 209)
|
||||
covergroup cg3;
|
||||
cp_data3: coverpoint data3 {
|
||||
option.auto_bin_max = 2; // coverpoint-level: creates 2 bins [0:3],[4:7]
|
||||
option.at_least = 3; // coverpoint-level at_least
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Test 4: range-bin skip path (lines 287, 356-359).
|
||||
// auto_bin_max=4 on 4-bit signal -> 4 range bins: [0:3],[4:7],[8:11],[12:15].
|
||||
// ignore_bins {[0:3]} excludes all values in the first range -> that bin is skipped.
|
||||
covergroup cg4;
|
||||
option.auto_bin_max = 4;
|
||||
cp: coverpoint data4 {
|
||||
ignore_bins ign = {[0:3]}; // first range excluded from coverage
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Test 5: 64-bit coverpoint exercises the width>=64 branches in createAutoBins
|
||||
// (V3Covergroup.cpp:269-270: maxVal/numTotalValues use UINT64_MAX sentinel)
|
||||
covergroup cg5;
|
||||
option.auto_bin_max = 4;
|
||||
cp_data64: coverpoint data64;
|
||||
endgroup
|
||||
|
||||
// Test type_option.auto_bin_max: treated same as option.auto_bin_max, creates 4 bins [0:1],[2:3],[4:5],[6:7]
|
||||
covergroup cg6;
|
||||
type_option.auto_bin_max = 4;
|
||||
cp_data3: coverpoint data3;
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg1 cg1_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
cg4 cg4_inst;
|
||||
cg5 cg5_inst;
|
||||
cg6 cg6_inst;
|
||||
|
||||
cg1_inst = new;
|
||||
cg2_inst = new;
|
||||
cg3_inst = new;
|
||||
cg4_inst = new;
|
||||
cg5_inst = new;
|
||||
cg6_inst = new;
|
||||
|
||||
data3 = 0; cg1_inst.sample();
|
||||
data3 = 3; cg1_inst.sample();
|
||||
|
||||
data3 = 0; cg2_inst.sample();
|
||||
data3 = 4; cg2_inst.sample();
|
||||
|
||||
data3 = 1; cg3_inst.sample();
|
||||
data3 = 5; cg3_inst.sample();
|
||||
|
||||
// Sample valid (non-ignored) values for cg4
|
||||
data4 = 4; cg4_inst.sample(); // [4:7] bin
|
||||
data4 = 8; cg4_inst.sample(); // [8:11] bin
|
||||
data4 = 12; cg4_inst.sample(); // [12:15] bin
|
||||
|
||||
// Sample cg5 (64-bit): exercises width>=64 path in createAutoBins
|
||||
data64 = 64'h0; cg5_inst.sample();
|
||||
data64 = 64'h1111111111111111; cg5_inst.sample();
|
||||
data64 = 64'hffffffffffffffff; cg5_inst.sample();
|
||||
|
||||
data3 = 0; cg6_inst.sample();
|
||||
data3 = 7; cg6_inst.sample();
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
%Error: t/t_covergroup_auto_bin_max_bad.v:14:29: option.auto_bin_max must be a constant expression
|
||||
: ... note: In instance 't'
|
||||
14 | option.auto_bin_max = size_var;
|
||||
| ^~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2025 by Wilson Snyder.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
int size_var;
|
||||
logic [3:0] cp_expr;
|
||||
|
||||
// Error: option.auto_bin_max must be a constant expression (group level)
|
||||
covergroup cg;
|
||||
option.auto_bin_max = size_var;
|
||||
cp: coverpoint cp_expr;
|
||||
endgroup
|
||||
|
||||
cg cg_i = new;
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
cg.data.auto[0]: 1
|
||||
cg.data.auto[1]: 1
|
||||
cg.data.auto[2]: 1
|
||||
cg.data.auto[3]: 1
|
||||
cg2.data64.auto_0: 1
|
||||
cg2.data64.auto_1: 1
|
||||
cg_4bit.data4.auto[0]: 1
|
||||
cg_4bit.data4.auto[1]: 1
|
||||
cg_4bit.data4.auto[2]: 1
|
||||
cg_4bit.data4.auto[3]: 1
|
||||
cg_4bit_excl.data4.auto[0]: 1
|
||||
cg_4bit_excl.data4.auto[1]: 0
|
||||
cg_4bit_excl.data4.auto[2]: 1
|
||||
cg_4bit_excl.data4.auto[3]: 0
|
||||
cg_4bit_excl.data4.bad [ignore]: 0
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Test automatic bins: bins auto[N]
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
/* verilator lint_off CMPCONST */
|
||||
logic [2:0] data; // 3-bit: 0-7
|
||||
logic [3:0] data4; // 4-bit: exercises width<64 path in maxVal computation
|
||||
logic [63:0] data64; // 64-bit: exercises width>=64 (UINT64_MAX) path
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 4-bit auto bins: exercises (width < 64) path: maxVal = (1<<4)-1 = 15
|
||||
covergroup cg_4bit;
|
||||
coverpoint data4 {
|
||||
bins auto[4]; // Creates 4 bins: [0:3], [4:7], [8:11], [12:15]
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 4-bit auto bins with ignore_bins: exercises excluded-value skip path
|
||||
covergroup cg_4bit_excl;
|
||||
coverpoint data4 {
|
||||
ignore_bins bad = {0}; // value 0 excluded from auto expansion
|
||||
bins auto[4];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// auto_bin_max=2 on 64-bit: exercises numTotalValues=UINT64_MAX path
|
||||
covergroup cg2;
|
||||
option.auto_bin_max = 2;
|
||||
coverpoint data64;
|
||||
endgroup
|
||||
/* verilator lint_on CMPCONST */
|
||||
|
||||
initial begin
|
||||
automatic cg cg_inst = new;
|
||||
automatic cg_4bit cg4_inst = new;
|
||||
automatic cg_4bit_excl cg4e_inst = new;
|
||||
automatic cg2 cg2_inst = new;
|
||||
|
||||
// Sample 3-bit cg: one value per bin
|
||||
data = 0; cg_inst.sample();
|
||||
data = 2; cg_inst.sample();
|
||||
data = 5; cg_inst.sample();
|
||||
data = 7; cg_inst.sample();
|
||||
|
||||
// Sample 4-bit bins
|
||||
data4 = 0; cg4_inst.sample(); // bin [0:3]
|
||||
data4 = 7; cg4_inst.sample(); // bin [4:7]
|
||||
data4 = 10; cg4_inst.sample(); // bin [8:11]
|
||||
data4 = 14; cg4_inst.sample(); // bin [12:15]
|
||||
|
||||
// Sample 4-bit with exclusion (value 0 excluded; bins start at 1)
|
||||
data4 = 1; cg4e_inst.sample();
|
||||
data4 = 8; cg4e_inst.sample();
|
||||
|
||||
// Sample both 64-bit range bins
|
||||
data64 = 64'd0; cg2_inst.sample(); // auto[0]
|
||||
data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample(); // auto[1]
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -11,8 +11,11 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename,
|
||||
verilator_flags2=['--assert', '--timing', '--error-limit 1000'],
|
||||
fails=True)
|
||||
# Use the same .v file as the non-timing test
|
||||
test.top_filename = "t/t_covergroup_clocked_sample.v"
|
||||
|
||||
test.compile(v_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
%Error: t/t_covergroup_autobins_bad.v:17:12: Automatic bins array size must be a constant
|
||||
: ... note: In instance 't'
|
||||
17 | bins auto[size_var];
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_covergroup_autobins_bad.v:24:12: Automatic bins array size must be >= 1, got 0
|
||||
: ... note: In instance 't'
|
||||
24 | bins auto[0];
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:31:12: Automatic bins array size of 1001 exceeds limit of 1000
|
||||
: ... note: In instance 't'
|
||||
31 | bins auto[1001];
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin value list; values must be constants
|
||||
: ... note: In instance 't'
|
||||
43 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:44:32: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
44 | ignore_bins ign_range = {[0:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:38:12: Non-constant expression in array bins range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
38 | bins b[] = {[size_var:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:39:12: Non-constant expression in array bins range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
39 | bins b_mixed[] = {[0:size_var]};
|
||||
| ^~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:40:23: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
40 | bins b_range = {[size_var:4]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:41:24: Non-constant expression in bin range; range bounds must be constants
|
||||
: ... note: In instance 't'
|
||||
41 | bins b_range2 = {[0:size_var]};
|
||||
| ^
|
||||
%Error: t/t_covergroup_autobins_bad.v:42:18: Non-constant expression in bin range; values must be constants
|
||||
: ... note: In instance 't'
|
||||
42 | bins b2 = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin range; values must be constants
|
||||
: ... note: In instance 't'
|
||||
43 | ignore_bins ign = {size_var};
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_covergroup_autobins_bad.v:51:25: option.at_least must be a constant expression
|
||||
: ... note: In instance 't'
|
||||
51 | option.at_least = size_var;
|
||||
| ^~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
|
@ -12,7 +12,7 @@ import vltest_bootstrap
|
|||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename,
|
||||
verilator_flags2=['--assert --error-limit 1000'],
|
||||
verilator_flags2=['--error-limit 1000'],
|
||||
fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Tests for automatic bins error conditions
|
||||
|
||||
module t;
|
||||
int size_var;
|
||||
logic [3:0] cp_expr;
|
||||
|
||||
// Error: array size must be a constant
|
||||
covergroup cg1;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[size_var];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: array size must be >= 1 (zero)
|
||||
covergroup cg2;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[0];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: array size exceeds limit of 1000
|
||||
covergroup cg2b;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins auto[1001];
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: non-constant value in bin ranges
|
||||
covergroup cg3;
|
||||
cp1: coverpoint cp_expr {
|
||||
bins b[] = {[size_var:size_var]}; // non-constant array bins range (both bounds non-const)
|
||||
bins b_mixed[] = {[0:size_var]}; // non-constant array bins range (max bound non-const)
|
||||
bins b_range = {[size_var:4]}; // non-constant regular bin range (lhs non-const)
|
||||
bins b_range2 = {[0:size_var]}; // non-constant regular bin range (rhs non-const)
|
||||
bins b2 = {size_var}; // non-constant simple bin value
|
||||
ignore_bins ign = {size_var}; // non-constant ignore_bins value
|
||||
ignore_bins ign_range = {[0:size_var]}; // non-constant ignore_bins range (rhs non-const)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Error: non-constant coverpoint option value
|
||||
covergroup cg4;
|
||||
cp1: coverpoint cp_expr {
|
||||
option.at_least = size_var; // non-constant coverpoint option value
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg1 cg1_inst = new;
|
||||
cg2 cg2_inst = new;
|
||||
cg2b cg2b_inst = new;
|
||||
cg3 cg3_inst = new;
|
||||
cg4 cg4_inst = new;
|
||||
|
||||
initial $finish;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
cg.data.low: 3
|
||||
cg.data.zero: 1
|
||||
cg_db.cp.high: 1
|
||||
cg_db.cp.low: 1
|
||||
cg_mixed.opcode.arith: 1
|
||||
cg_mixed.opcode.load: 1
|
||||
cg_mixed.opcode.nop: 1
|
||||
cg_mixed.opcode.other: 1
|
||||
cg_mixed.opcode.store: 1
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(verilator_flags2=['--coverage'])
|
||||
|
||||
test.execute()
|
||||
|
||||
coverage_covergroup_common.covergroup_coverage_report(test)
|
||||
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
|
||||
|
||||
# Verify coverage.dat format contains covergroup entries (replaces t_covergroup_database)
|
||||
test.file_grep(test.coverage_filename, r'covergroup')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}low')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}high')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.low')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.high')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}low.*\' [1-9]')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}high.*\' [1-9]')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Tests bin counts, mixed bin types (single values, lists, ranges), and
|
||||
// coverage database format (verifies coverage.dat contains covergroup entries).
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
logic [3:0] data;
|
||||
logic [7:0] opcode;
|
||||
|
||||
// cg: basic bin count tracking
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins zero = {0};
|
||||
bins low = {[1:3]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_mixed: mixed bin types - single values, multi-value lists, ranges
|
||||
covergroup cg_mixed;
|
||||
coverpoint opcode {
|
||||
bins nop = {8'h00};
|
||||
bins load = {8'h01, 8'h02, 8'h03};
|
||||
bins store = {8'h04, 8'h05};
|
||||
bins arith = {[8'h10:8'h1F]};
|
||||
bins other = {[8'h20:8'hFE]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// cg_db: labeled coverpoint exercises cg.cp.low/high hierarchy in coverage.dat
|
||||
covergroup cg_db;
|
||||
cp: coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[8:15]};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst;
|
||||
cg_mixed cg_mixed_inst;
|
||||
cg_db cg_db_inst;
|
||||
|
||||
initial begin
|
||||
cg_inst = new;
|
||||
cg_mixed_inst = new;
|
||||
cg_db_inst = new;
|
||||
|
||||
data = 0; cg_inst.sample(); // zero: 1
|
||||
data = 1; cg_inst.sample(); // low: 1
|
||||
data = 2; cg_inst.sample(); // low: 2
|
||||
data = 2; cg_inst.sample(); // low: 3
|
||||
|
||||
opcode = 8'h00; cg_mixed_inst.sample(); // nop
|
||||
opcode = 8'h02; cg_mixed_inst.sample(); // load
|
||||
opcode = 8'h05; cg_mixed_inst.sample(); // store
|
||||
opcode = 8'h15; cg_mixed_inst.sample(); // arith
|
||||
opcode = 8'h80; cg_mixed_inst.sample(); // other
|
||||
|
||||
data = 1; cg_db_inst.sample(); // low
|
||||
data = 10; cg_db_inst.sample(); // high
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
cg.cp_data.one: 1
|
||||
cg.cp_data.three: 1
|
||||
cg.cp_data.two: 1
|
||||
cg.cp_data.zero: 2
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Test covergroup clocked (automatic) sampling
|
||||
// Tests --no-timing (default) mode; see t_covergroup_auto_sample_timing for --timing variant.
|
||||
// This file ONLY is placed into the Public Domain, for any use, without warranty.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
logic [1:0] data;
|
||||
|
||||
// Covergroup with automatic sampling on posedge clk
|
||||
covergroup cg @(posedge clk);
|
||||
cp_data: coverpoint data {
|
||||
bins zero = {2'b00};
|
||||
bins one = {2'b01};
|
||||
bins two = {2'b10};
|
||||
bins three = {2'b11};
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst = new;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
|
||||
case (cyc)
|
||||
0: data <= 2'b00;
|
||||
1: data <= 2'b01;
|
||||
2: data <= 2'b10;
|
||||
3: data <= 2'b11;
|
||||
4: begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endcase
|
||||
|
||||
// NOTE: NO manual .sample() call - relying on automatic sampling!
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_coverpoint_method_unsup.v:21:32: Unsupported: 'bins' explicit array size (treated as '[]')
|
||||
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: t/t_covergroup_coverpoint_method_unsup.v:31:42: Member 'a' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_covergroup_coverpoint_method_unsup.v:32:42: Member 'b' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_covergroup_coverpoint_method_unsup.v:33:18: Member 'a' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_covergroup_coverpoint_method_unsup.v:34:18: Member 'b' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:17: Ignoring unsupported: coverage clocking event
|
||||
19 | covergroup cg @(posedge clk);
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:20:5: Ignoring unsupported: coverpoint
|
||||
20 | coverpoint a;
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:36: Ignoring unsupported: cover bin specification
|
||||
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
|
||||
| ^
|
||||
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:5: Ignoring unsupported: coverpoint
|
||||
21 | coverpoint b {bins the_bins[5] = {[0 : 20]};}
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:3: Ignoring unsupported: covergroup
|
||||
19 | covergroup cg @(posedge clk);
|
||||
| ^~~~~~~~~~
|
||||
%Error: t/t_covergroup_coverpoints_unsup.v:31:42: Member 'a' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
31 | $display("coverage a = %f", the_cg.a.get_inst_coverage());
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_covergroup_coverpoints_unsup.v:32:42: Member 'b' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
32 | $display("coverage b = %f", the_cg.b.get_inst_coverage());
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_covergroup_coverpoints_unsup.v:33:18: Member 'a' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop();
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_covergroup_coverpoints_unsup.v:34:18: Member 'b' not found in covergroup 'cg'
|
||||
: ... note: In instance 't'
|
||||
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit''
|
||||
: ... note: In instance 't'
|
||||
34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop();
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
cg2.addr_cmd.addr0_x_read [cross]: 1
|
||||
cg2.addr_cmd.addr0_x_write [cross]: 1
|
||||
cg2.addr_cmd.addr1_x_read [cross]: 1
|
||||
cg2.addr_cmd.addr1_x_write [cross]: 1
|
||||
cg2.cp_addr.addr0: 2
|
||||
cg2.cp_addr.addr1: 2
|
||||
cg2.cp_cmd.read: 2
|
||||
cg2.cp_cmd.write: 2
|
||||
cg3.addr_cmd_mode.addr0_x_read_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr0_x_read_x_normal [cross]: 1
|
||||
cg3.addr_cmd_mode.addr0_x_write_x_debug [cross]: 1
|
||||
cg3.addr_cmd_mode.addr0_x_write_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_read_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_read_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_write_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr1_x_write_x_normal [cross]: 1
|
||||
cg3.addr_cmd_mode.addr2_x_read_x_debug [cross]: 1
|
||||
cg3.addr_cmd_mode.addr2_x_read_x_normal [cross]: 0
|
||||
cg3.addr_cmd_mode.addr2_x_write_x_debug [cross]: 0
|
||||
cg3.addr_cmd_mode.addr2_x_write_x_normal [cross]: 0
|
||||
cg3.cp_addr.addr0: 2
|
||||
cg3.cp_addr.addr1: 1
|
||||
cg3.cp_addr.addr2: 1
|
||||
cg3.cp_cmd.read: 2
|
||||
cg3.cp_cmd.write: 2
|
||||
cg3.cp_mode.debug: 2
|
||||
cg3.cp_mode.normal: 2
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd [cross]: 1
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even [cross]: 0
|
||||
cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd [cross]: 1
|
||||
cg4.cp_addr.addr0: 2
|
||||
cg4.cp_addr.addr1: 2
|
||||
cg4.cp_cmd.read: 2
|
||||
cg4.cp_cmd.write: 2
|
||||
cg4.cp_mode.debug: 2
|
||||
cg4.cp_mode.normal: 2
|
||||
cg4.cp_parity.even: 2
|
||||
cg4.cp_parity.odd: 2
|
||||
cg5.addr_cmd_opt.addr0_x_read [cross]: 1
|
||||
cg5.addr_cmd_opt.addr0_x_write [cross]: 0
|
||||
cg5.addr_cmd_opt.addr1_x_read [cross]: 0
|
||||
cg5.addr_cmd_opt.addr1_x_write [cross]: 1
|
||||
cg5.cp_addr.addr0: 1
|
||||
cg5.cp_addr.addr1: 1
|
||||
cg5.cp_cmd.read: 1
|
||||
cg5.cp_cmd.write: 1
|
||||
cg_at_least.addr_cmd_al.addr0_x_read [cross]: 1
|
||||
cg_at_least.addr_cmd_al.addr0_x_write [cross]: 0
|
||||
cg_at_least.addr_cmd_al.addr1_x_read [cross]: 0
|
||||
cg_at_least.addr_cmd_al.addr1_x_write [cross]: 1
|
||||
cg_at_least.cp_addr.addr0: 1
|
||||
cg_at_least.cp_addr.addr1: 1
|
||||
cg_at_least.cp_cmd.read: 1
|
||||
cg_at_least.cp_cmd.write: 1
|
||||
cg_goal.addr_cmd_goal.addr0_x_read [cross]: 1
|
||||
cg_goal.addr_cmd_goal.addr0_x_write [cross]: 0
|
||||
cg_goal.addr_cmd_goal.addr1_x_read [cross]: 0
|
||||
cg_goal.addr_cmd_goal.addr1_x_write [cross]: 1
|
||||
cg_goal.cp_addr.addr0: 1
|
||||
cg_goal.cp_addr.addr1: 1
|
||||
cg_goal.cp_cmd.read: 1
|
||||
cg_goal.cp_cmd.write: 1
|
||||
cg_ignore.cp_addr.a0: 2
|
||||
cg_ignore.cp_addr.a1: 2
|
||||
cg_ignore.cp_addr.ign [ignore]: 1
|
||||
cg_ignore.cp_cmd.read: 3
|
||||
cg_ignore.cp_cmd.write: 2
|
||||
cg_ignore.cross_ab.a0_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a0_x_write [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_read [cross]: 1
|
||||
cg_ignore.cross_ab.a1_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_read [cross]: 1
|
||||
cg_range.addr_cmd_range.hi_range_x_write [cross]: 1
|
||||
cg_range.addr_cmd_range.lo_range_x_read [cross]: 1
|
||||
cg_range.addr_cmd_range.lo_range_x_write [cross]: 1
|
||||
cg_range.cp_addr.hi_range: 2
|
||||
cg_range.cp_addr.lo_range: 2
|
||||
cg_range.cp_cmd.read: 2
|
||||
cg_range.cp_cmd.write: 2
|
||||
cg_unnamed_cross.__cross7.a0_x_read [cross]: 1
|
||||
cg_unnamed_cross.__cross7.a0_x_write [cross]: 0
|
||||
cg_unnamed_cross.__cross7.a1_x_read [cross]: 0
|
||||
cg_unnamed_cross.__cross7.a1_x_write [cross]: 1
|
||||
cg_unnamed_cross.cp_a.a0: 1
|
||||
cg_unnamed_cross.cp_a.a1: 1
|
||||
cg_unnamed_cross.cp_c.read: 1
|
||||
cg_unnamed_cross.cp_c.write: 1
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_read [cross]: 1
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_write [cross]: 0
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_read [cross]: 0
|
||||
cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_write [cross]: 1
|
||||
cg_unsup_cross_opt.cp_addr.addr0: 1
|
||||
cg_unsup_cross_opt.cp_addr.addr1: 1
|
||||
cg_unsup_cross_opt.cp_cmd.read: 1
|
||||
cg_unsup_cross_opt.cp_cmd.write: 1
|
||||
|
|
@ -0,0 +1,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
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
coverage_covergroup_common.run(
|
||||
test, verilator_flags2=['--dumpi-tree 3 --dumpi-tree-json 3 --Wno-COVERIGN'])
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test cross coverage: 2-way, 3-way, and 4-way crosses
|
||||
|
||||
module t;
|
||||
logic [1:0] addr;
|
||||
logic cmd;
|
||||
logic mode;
|
||||
logic parity;
|
||||
|
||||
// 2-way cross
|
||||
covergroup cg2;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// 3-way cross
|
||||
covergroup cg3;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
bins addr2 = {2};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cp_mode: coverpoint mode {
|
||||
bins normal = {0};
|
||||
bins debug = {1};
|
||||
}
|
||||
addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode;
|
||||
endgroup
|
||||
|
||||
// 4-way cross
|
||||
covergroup cg4;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cp_mode: coverpoint mode {
|
||||
bins normal = {0};
|
||||
bins debug = {1};
|
||||
}
|
||||
cp_parity: coverpoint parity {
|
||||
bins even = {0};
|
||||
bins odd = {1};
|
||||
}
|
||||
addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity;
|
||||
endgroup
|
||||
|
||||
// Cross with option inside body: exercises addOptionsp in visit(AstCoverCross*)
|
||||
covergroup cg5;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_opt: cross cp_addr, cp_cmd {
|
||||
option.weight = 2;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 2-way cross with range bin: exercises lo!=hi path in buildBinCondition
|
||||
covergroup cg_range;
|
||||
cp_addr: coverpoint addr {
|
||||
bins lo_range = {[0:1]}; // range bin (lo != hi) -> makeRangeCondition path
|
||||
bins hi_range = {[2:3]};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_range: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross where one coverpoint has ignore_bins: exercises BINS_USER FALSE branch
|
||||
// in collectBins during cross code generation (L1139)
|
||||
covergroup cg_ignore;
|
||||
cp_addr: coverpoint addr {
|
||||
ignore_bins ign = {3}; // BINS_IGNORE: not BINS_USER, exercises L1139 FALSE path
|
||||
bins a0 = {0};
|
||||
bins a1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
cross_ab: cross cp_addr, cp_cmd;
|
||||
endgroup
|
||||
|
||||
// Cross with at_least option in body: covers AT_LEAST case in V3LinkParse visit(AstCoverCross*)
|
||||
covergroup cg_at_least;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_al: cross cp_addr, cp_cmd {
|
||||
option.at_least = 3;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Cross with goal option in body: covers GOAL case in V3LinkParse visit(AstCoverCross*)
|
||||
covergroup cg_goal;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_goal: cross cp_addr, cp_cmd {
|
||||
option.goal = 90;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Cross with unsupported option: covers COVERIGN warning in V3LinkParse visit(AstCoverCross*)
|
||||
covergroup cg_unsup_cross_opt;
|
||||
cp_addr: coverpoint addr {
|
||||
bins addr0 = {0};
|
||||
bins addr1 = {1};
|
||||
}
|
||||
cp_cmd: coverpoint cmd {
|
||||
bins read = {0};
|
||||
bins write = {1};
|
||||
}
|
||||
addr_cmd_unsup: cross cp_addr, cp_cmd {
|
||||
option.per_instance = 1; // unsupported for cross; triggers COVERIGN at V3LinkParse:1380
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with unnamed cross: exercises crossName.empty() fallback to "cross" (L1395)
|
||||
covergroup cg_unnamed_cross;
|
||||
cp_a: coverpoint addr { bins a0 = {0}; bins a1 = {1}; }
|
||||
cp_c: coverpoint cmd { bins read = {0}; bins write = {1}; }
|
||||
cross cp_a, cp_c; // no label -> crossName is empty
|
||||
endgroup
|
||||
|
||||
cg2 cg2_inst = new;
|
||||
cg_ignore cg_ignore_inst = new;
|
||||
cg_range cg_range_inst = new;
|
||||
cg3 cg3_inst = new;
|
||||
cg4 cg4_inst = new;
|
||||
cg5 cg5_inst = new;
|
||||
cg_at_least cg_at_least_inst = new;
|
||||
cg_goal cg_goal_inst = new;
|
||||
cg_unsup_cross_opt cg_unsup_cross_opt_inst = new;
|
||||
cg_unnamed_cross cg_unnamed_cross_inst = new;
|
||||
|
||||
initial begin
|
||||
// Sample 2-way: hit all 4 combinations
|
||||
addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write
|
||||
addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write
|
||||
addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read
|
||||
|
||||
// Sample 3-way: hit 4 of 12 combinations
|
||||
addr = 0; cmd = 0; mode = 0; cg3_inst.sample(); // addr0 x read x normal
|
||||
addr = 1; cmd = 1; mode = 0; cg3_inst.sample(); // addr1 x write x normal
|
||||
addr = 2; cmd = 0; mode = 1; cg3_inst.sample(); // addr2 x read x debug
|
||||
addr = 0; cmd = 1; mode = 1; cg3_inst.sample(); // addr0 x write x debug
|
||||
|
||||
// Sample 4-way: hit 4 of 16 combinations
|
||||
addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample();
|
||||
addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample();
|
||||
addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample();
|
||||
addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample();
|
||||
|
||||
// Sample cg5 (cross with option)
|
||||
addr = 0; cmd = 0; cg5_inst.sample();
|
||||
addr = 1; cmd = 1; cg5_inst.sample();
|
||||
|
||||
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
|
||||
addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read
|
||||
addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write
|
||||
addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write
|
||||
addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read
|
||||
addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins)
|
||||
|
||||
// Sample range-bin cross
|
||||
addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read
|
||||
addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write
|
||||
addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write
|
||||
addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read
|
||||
|
||||
// Sample cg_at_least (option.at_least in cross body)
|
||||
addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write
|
||||
|
||||
// Sample cg_goal (option.goal in cross body)
|
||||
addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write
|
||||
|
||||
// Sample cg_unsup_cross_opt: option.per_instance triggers COVERIGN in V3LinkParse
|
||||
addr = 0; cmd = 0; cg_unsup_cross_opt_inst.sample(); // addr0 x read
|
||||
addr = 1; cmd = 1; cg_unsup_cross_opt_inst.sample(); // addr1 x write
|
||||
|
||||
// Sample cg_unnamed_cross: exercises unnamed cross (crossName fallback to "cross")
|
||||
addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read
|
||||
addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:10: Ignoring unsupported coverage cross option: 'per_instance'
|
||||
13 | option.per_instance = 1;
|
||||
| ^~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2025 by Wilson Snyder.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename, fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,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
|
||||
|
||||
module t;
|
||||
covergroup cg;
|
||||
cp_a: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
|
||||
cp_b: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; }
|
||||
cross_ab: cross cp_a, cp_b {
|
||||
option.per_instance = 1; // unsupported for cross; triggers COVERIGN
|
||||
}
|
||||
endgroup
|
||||
cg cg_i = new;
|
||||
initial begin
|
||||
cg_i.sample();
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.other: 2
|
||||
cg2.cp_only_default.all: 4
|
||||
cg3.data.bad [ignore]: 1
|
||||
cg3.data.err [illegal]: 0
|
||||
cg3.data.normal: 2
|
||||
cg3.data.other: 2
|
||||
cg4.cp_idx.auto_0: 1
|
||||
cg4.cp_idx.auto_1: 1
|
||||
cg4.cp_idx.auto_2: 1
|
||||
cg4.cp_idx.skip [ignore]: 0
|
||||
cg5.cp_data64.auto[0]: 2
|
||||
cg5.cp_data64.auto[1]: 0
|
||||
cg5.cp_data64.auto[2]: 0
|
||||
cg5.cp_data64.auto[3]: 0
|
||||
cg6.cp_data65.hi: 1
|
||||
cg6.cp_data65.lo: 1
|
||||
cg7.data.hi: 1
|
||||
cg7.data.lo: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test default bins - catch-all for values not in other bins
|
||||
|
||||
// Non-covergroup class: exercises V3Active isCovergroup()=false branch
|
||||
class DataHelper;
|
||||
bit [7:0] val;
|
||||
function new(bit [7:0] v); val = v; endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
bit [7:0] data;
|
||||
logic [1:0] idx;
|
||||
logic [63:0] data64; // 64-bit: exercises width>=64 auto-bin path (L139)
|
||||
logic [64:0] data65; // 65-bit: exercises exprWidth>64 in makeRangeCondition
|
||||
DataHelper helper; // Module-level class var: exercises V3Active isCovergroup()=false
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[12:15]};
|
||||
bins other = default; // Catches everything else (4-11, 16+)
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with default as the ONLY bin: exercises defaultCondp=BitTrue path
|
||||
covergroup cg2;
|
||||
cp_only_default: coverpoint data {
|
||||
bins all = default;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with default + ignore + illegal bins: exercises BINS_IGNORE/BINS_ILLEGAL
|
||||
// skip paths in generateDefaultBinMatchCode (L558-L559)
|
||||
covergroup cg3;
|
||||
coverpoint data {
|
||||
ignore_bins bad = {255}; // BINS_IGNORE skip path
|
||||
illegal_bins err = {254}; // BINS_ILLEGAL skip path
|
||||
bins normal = {[1:10]};
|
||||
bins other = default;
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Covergroup with auto-bins + ignore_bins on small range: exercises L295 excluded-value continue
|
||||
// When numValidValues <= auto_bin_max, single-value auto-bins are created per value; the
|
||||
// excluded.find() check at L295 fires for the ignore_bins value (idx=2).
|
||||
covergroup cg4;
|
||||
cp_idx: coverpoint idx {
|
||||
ignore_bins skip = {2}; // value 2 excluded; auto-bins created for 0,1,3
|
||||
}
|
||||
endgroup
|
||||
|
||||
// 64-bit signal with 4 auto-bins: exercises width>=64 branch in auto-bin range calculation
|
||||
covergroup cg5;
|
||||
cp_data64: coverpoint data64 { bins auto[4]; }
|
||||
endgroup
|
||||
|
||||
// 65-bit signal with range bins: exercises exprWidth>64 path in makeRangeCondition
|
||||
covergroup cg6;
|
||||
cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; }
|
||||
endgroup
|
||||
|
||||
// Unlabeled coverpoint: exercises cpName fallback via exprp()->name() (L1394-1398)
|
||||
covergroup cg7;
|
||||
coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; }
|
||||
endgroup
|
||||
|
||||
initial begin
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
cg3 cg3_inst;
|
||||
cg4 cg4_inst;
|
||||
cg5 cg5_inst;
|
||||
cg6 cg6_inst;
|
||||
cg7 cg7_inst;
|
||||
|
||||
cg_inst = new();
|
||||
cg2_inst = new();
|
||||
cg3_inst = new();
|
||||
cg4_inst = new();
|
||||
cg5_inst = new();
|
||||
cg6_inst = new();
|
||||
cg7_inst = new();
|
||||
helper = new(8'h42);
|
||||
data = helper.val; // Use helper to avoid optimization
|
||||
|
||||
// Hit low bin
|
||||
data = 2;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
|
||||
// Hit high bin
|
||||
data = 14;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
|
||||
// Hit default bin with value 7 (not in low or high)
|
||||
data = 7;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
|
||||
// Hit another default value (should not increase coverage)
|
||||
data = 20;
|
||||
cg_inst.sample();
|
||||
cg2_inst.sample();
|
||||
|
||||
// Sample cg3: exercises BINS_IGNORE/BINS_ILLEGAL skip in default-bin detection loop
|
||||
data = 2; cg3_inst.sample(); // hits normal bin
|
||||
data = 7; cg3_inst.sample(); // hits normal bin again
|
||||
data = 255; cg3_inst.sample(); // ignore_bins (not counted)
|
||||
// note: do not sample 254 (illegal_bins would cause runtime assertion)
|
||||
data = 100; cg3_inst.sample(); // hits default (other) bin
|
||||
|
||||
// Sample cg4: exercises auto-bin generation with excluded value (L295)
|
||||
// idx=2 is in ignore_bins, so auto-bins cover 0,1,3 only
|
||||
idx = 0; cg4_inst.sample();
|
||||
idx = 1; cg4_inst.sample();
|
||||
idx = 3; cg4_inst.sample();
|
||||
|
||||
// Sample cg5: 64-bit signal, sample into bin b[0] (value 0 is in first quarter)
|
||||
data64 = 0; cg5_inst.sample();
|
||||
data64 = 5; cg5_inst.sample();
|
||||
|
||||
// Sample cg6: 65-bit signal with range bins
|
||||
data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15]
|
||||
data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200]
|
||||
|
||||
// Sample cg7: unlabeled coverpoint (exercises exprp()->name() path)
|
||||
data = 3; cg7_inst.sample(); // hits bin lo
|
||||
data = 10; cg7_inst.sample(); // hits bin hi
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Empty covergroup coverage: 100.000000%
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module - Edge case: empty covergroup
|
||||
// This file ONLY is placed into the Public Domain, for any use, without warranty.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test: Empty covergroup (no coverpoints)
|
||||
// Expected: Should compile, coverage should be 100% (nothing to cover)
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
logic [7:0] value;
|
||||
|
||||
// Empty covergroup - no coverpoints defined
|
||||
covergroup cg_empty;
|
||||
// Intentionally empty
|
||||
endgroup
|
||||
|
||||
cg_empty cg_inst = new;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
value <= value + 1;
|
||||
|
||||
cg_inst.sample();
|
||||
|
||||
if (cyc == 5) begin
|
||||
real cov;
|
||||
cov = cg_inst.get_inst_coverage();
|
||||
$display("Empty covergroup coverage: %f%%", cov);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (cyc > 10) begin
|
||||
$display("ERROR: Test timed out");
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
class base;
|
||||
enum {red, green, blue} color;
|
||||
covergroup g1 (bit [3:0] a) with function sample(bit b);
|
||||
option.weight = 10;
|
||||
option.per_instance = 1;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
c: coverpoint color;
|
||||
endgroup
|
||||
function new();
|
||||
g1 = new(0);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class derived extends base;
|
||||
bit d;
|
||||
covergroup extends g1;
|
||||
option.weight = 1; // overrides the weight from base g1
|
||||
// uses per_instance = 1 from base g1
|
||||
c: coverpoint color // overrides the c coverpoint in base g1
|
||||
{
|
||||
ignore_bins ignore = {blue};
|
||||
}
|
||||
coverpoint d; // adds new coverpoint
|
||||
cross a, d; // crosses new coverpoint with inherited one
|
||||
endgroup :g1
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
endclass
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_covergroup_extends_newfirst.v:29:5: Unsupported: covergroup inheritance (extends) is not implemented
|
||||
29 | covergroup extends g1;
|
||||
| ^~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
class base;
|
||||
function new();
|
||||
g1 = new(0);
|
||||
endfunction
|
||||
enum {red, green, blue} color;
|
||||
covergroup g1 (bit [3:0] a) with function sample(bit b);
|
||||
option.weight = 10;
|
||||
option.per_instance = 1;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
c: coverpoint color;
|
||||
endgroup
|
||||
endclass
|
||||
|
||||
class derived extends base;
|
||||
bit d;
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
covergroup extends g1;
|
||||
option.weight = 1; // overrides the weight from base g1
|
||||
// uses per_instance = 1 from base g1
|
||||
c: coverpoint color // overrides the c coverpoint in base g1
|
||||
{
|
||||
ignore_bins ignore = {blue};
|
||||
}
|
||||
coverpoint d; // adds new coverpoint
|
||||
cross a, d; // crosses new coverpoint with inherited one
|
||||
endgroup :g1
|
||||
endclass
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
cg_array_iff.cp.arr: 1
|
||||
cg_default_iff.cp.def: 1
|
||||
cg_default_iff.cp.known: 1
|
||||
cg_iff.cp_value.disabled_hi: 0
|
||||
cg_iff.cp_value.disabled_lo: 0
|
||||
cg_iff.cp_value.enabled_hi: 1
|
||||
cg_iff.cp_value.enabled_lo: 1
|
||||
cg_trans2_iff.cp.t2: 1
|
||||
cg_trans3_iff.cp.t3: 1
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by Wilson Snyder.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test iff (enable) guard: sampling is gated by the enable condition.
|
||||
// Covers iff on explicit value bins, default bin, array bins,
|
||||
// simple 2-step transition, and 3-step transition.
|
||||
|
||||
module t;
|
||||
logic enable;
|
||||
int value;
|
||||
|
||||
// --- Original: iff on explicit value bins (lines 565-567) ---
|
||||
covergroup cg_iff;
|
||||
cp_value: coverpoint value iff (enable) {
|
||||
bins disabled_lo = {1};
|
||||
bins disabled_hi = {2};
|
||||
bins enabled_lo = {3};
|
||||
bins enabled_hi = {4};
|
||||
}
|
||||
endgroup
|
||||
|
||||
// --- iff on default bin (lines 633-635/641) ---
|
||||
covergroup cg_default_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins known = {10};
|
||||
bins def = default; // default bin with coverpoint-level iff
|
||||
}
|
||||
endgroup
|
||||
|
||||
// --- iff on array bins (lines 982-983) ---
|
||||
covergroup cg_array_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins arr[] = {5, 6, 7}; // array bins, all gated by iff
|
||||
}
|
||||
endgroup
|
||||
|
||||
// --- iff on 2-step transition (lines 1109-1110) ---
|
||||
covergroup cg_trans2_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins t2 = (1 => 2);
|
||||
}
|
||||
endgroup
|
||||
|
||||
// --- iff on 3-step transition (lines 724-725, 762-763) ---
|
||||
covergroup cg_trans3_iff;
|
||||
cp: coverpoint value iff (enable) {
|
||||
bins t3 = (1 => 2 => 3);
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg_iff cg1 = new;
|
||||
cg_default_iff cg2 = new;
|
||||
cg_array_iff cg3 = new;
|
||||
cg_trans2_iff cg4 = new;
|
||||
cg_trans3_iff cg5 = new;
|
||||
|
||||
initial begin
|
||||
// Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded
|
||||
enable = 0;
|
||||
value = 1; cg1.sample();
|
||||
value = 2; cg1.sample();
|
||||
|
||||
// Sample enabled_lo and enabled_hi with enable=1 -- must be recorded
|
||||
enable = 1;
|
||||
value = 3; cg1.sample();
|
||||
value = 4; cg1.sample();
|
||||
|
||||
// cg2: default bin -- enable=1 lets known and default through
|
||||
enable = 1;
|
||||
value = 10; cg2.sample(); // hits 'known'
|
||||
value = 99; cg2.sample(); // hits 'def' (default)
|
||||
enable = 0;
|
||||
value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def'
|
||||
|
||||
// cg3: array bins with iff
|
||||
enable = 1;
|
||||
value = 5; cg3.sample(); // arr[5] hit
|
||||
enable = 0;
|
||||
value = 6; cg3.sample(); // gated
|
||||
|
||||
// cg4: 2-step transition with iff
|
||||
enable = 1;
|
||||
value = 1; cg4.sample();
|
||||
value = 2; cg4.sample(); // (1=>2) hit with enable=1
|
||||
enable = 0;
|
||||
value = 1; cg4.sample();
|
||||
value = 2; cg4.sample(); // (1=>2) gated by iff
|
||||
|
||||
// cg5: 3-step transition with iff
|
||||
enable = 1;
|
||||
value = 1; cg5.sample();
|
||||
value = 2; cg5.sample(); // mid-sequence, enable=1
|
||||
enable = 0;
|
||||
value = 3; cg5.sample(); // iff fails at step 3 -- triggers restart path (line 762-763)
|
||||
enable = 1;
|
||||
value = 1; cg5.sample();
|
||||
value = 2; cg5.sample();
|
||||
value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
cg.data.arr [ignore]: 0
|
||||
cg.data.bad [illegal]: 0
|
||||
cg.data.catch_all [ignore]: 0
|
||||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.reserved [ignore]: 1
|
||||
cg.data.wib: 0
|
||||
cg2.cp_auto.auto_0: 1
|
||||
cg2.cp_auto.auto_1: 1
|
||||
cg2.cp_auto.ign [ignore]: 2
|
||||
cg2.cp_auto.ign_trans [ignore]: 1
|
||||
cg2.cp_bounds.hi: 2
|
||||
cg2.cp_bounds.lo: 2
|
||||
cg2.cp_full.all: 4
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
import coverage_covergroup_common
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
coverage_covergroup_common.run(test)
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Matthew Ballance
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test ignore_bins - excluded from coverage
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
logic [3:0] data;
|
||||
logic [1:0] data2; // 2-bit signal for range-boundary tests
|
||||
|
||||
covergroup cg;
|
||||
coverpoint data {
|
||||
bins low = {[0:3]};
|
||||
bins high = {[8:11]};
|
||||
ignore_bins reserved = {[12:15]};
|
||||
ignore_bins catch_all = default; // null rangesp: exercises generateBinMatchCode !fullCondp
|
||||
ignore_bins arr[] = {4, 5}; // array form: exercises bins_orBraE $3 true branch
|
||||
wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore bins (L7084-7085)
|
||||
illegal_bins bad[] = {6, 7}; // illegal array form: exercises bins_orBraE $3 true branch
|
||||
}
|
||||
endgroup
|
||||
|
||||
// Exercises:
|
||||
// extractValuesFromRange AstInsideRange branch (ignore_bins range, no regular bins)
|
||||
// createImplicitAutoBins with excluded range values
|
||||
// makeRangeCondition: skipUpperCheck=true (hi=maxVal) and both-skip (BitTrue)
|
||||
// ignore_bins with transition list (L7102-7104)
|
||||
covergroup cg2;
|
||||
cp_auto: coverpoint data2 {
|
||||
ignore_bins ign = {[2:3]}; // range ignore, no regular bins -> auto-bins created
|
||||
ignore_bins ign_trans = (0 => 1); // ignore_bins with transition (L7102-7104)
|
||||
}
|
||||
cp_bounds: coverpoint data2 {
|
||||
bins lo = {[0:1]}; // lo=0: skipLowerCheck -> AstLte
|
||||
bins hi = {[2:3]}; // hi=maxVal (2-bit): skipUpperCheck -> AstGte
|
||||
}
|
||||
cp_full: coverpoint data2 {
|
||||
bins all = {[0:3]}; // lo=0 and hi=maxVal: both skip -> AstConst(BitTrue)
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg cg_inst;
|
||||
cg2 cg2_inst;
|
||||
|
||||
initial begin
|
||||
cg_inst = new;
|
||||
cg2_inst = new;
|
||||
|
||||
data = 13; cg_inst.sample(); // reserved - ignored
|
||||
data = 1; cg_inst.sample(); // low
|
||||
data = 10; cg_inst.sample(); // high
|
||||
|
||||
data2 = 0; cg2_inst.sample(); // auto_0, lo, all
|
||||
data2 = 1; cg2_inst.sample(); // auto_1, lo, all
|
||||
data2 = 2; cg2_inst.sample(); // ign, hi, all
|
||||
data2 = 3; cg2_inst.sample(); // ign, hi, all
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
cg.data.forbidden [illegal]: 0
|
||||
cg.data.high: 1
|
||||
cg.data.low: 1
|
||||
cg.data.mid: 1
|
||||
cg2.cp_arr.bad_arr [illegal]: 0
|
||||
cg2.cp_arr.ok: 1
|
||||
cg2.cp_arr.wlib: 0
|
||||
cg2.cp_trans.bad_2step [illegal]: 0
|
||||
cg2.cp_trans.bad_3step [illegal]: 0
|
||||
cg2.cp_trans.lib_default [illegal]: 0
|
||||
cg2.cp_trans.ok: 1
|
||||
cg3.cp.ign [ignore]: 2
|
||||
cg3.cp.ill [illegal]: 0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue