Compare commits

..

13 Commits

Author SHA1 Message Date
Jonathan Schröter de30e6f590
Merge e449552777 into 9d74984163 2025-11-06 10:46:11 -08:00
Geza Lore 9d74984163 Fix non-deterministic output when splitting Syms file 2025-11-06 15:46:14 +00:00
Bartłomiej Chmiel 5adecb9fa3
Support multi-expression sequences (#6639) 2025-11-06 08:42:27 -05:00
Geza Lore f7e12e9219
Fix slow compilation of generated sampled value code (#6652)
For handling $past and similar functions, we used to collect sampled
values of variables at the beginning of the main _eval function. If we
have many of these, this can grow _eval very large which can make C++
compilation very slow. Apply usual fix of emitting the necessary code in
a separate function and then splitting it based on size.
2025-11-06 13:31:40 +00:00
Todd Strader 47b52800bf
Fix expression coverage of system calls (#6592) 2025-11-06 08:23:35 -05:00
Christian Hecken 100c831474
Tests: Fix Icarus vvp execution with use_libvpi (#6648) 2025-11-05 13:41:53 -05:00
Geza Lore cb5f038060
Internals: Optimzie unlinking rest of list after head (tail) (#6643)
AstNode::unlinkFrBackWithNext is O(n) if the subject node is not the
head of the list. We sometimes want to unlink the rest of the list
starting at the node after the head (e.g.: in
V3Sched::util::splitCheck), this patch makes that O(1) as well.
2025-11-05 15:55:30 +00:00
Geza Lore a35d4a4b4b
Add memory usage statistics on macOS (#6644)
Add memory usage statistics on macOS
2025-11-05 15:45:35 +00:00
Ryszard Rozak 96ece751fa
Internals: Simplify release handling. No functional change intended (#6647) 2025-11-05 10:20:18 -05:00
Geza Lore 4404978765
Fix command line statistics with --stats (#6645) (#6646)
Fixes #6645
2025-11-05 14:59:25 +00:00
Yilou Wang 0853aa7515
Support basic global constraints (#6551) (#6552) 2025-11-05 07:14:03 -05:00
github action 574c69c092 Apply 'make format' 2025-11-05 10:50:31 +00:00
Jens Yuechao Liu e2f5854088
Fix slice memory overflow on large output arrays (#6636) (#6638) 2025-11-05 05:48:22 -05:00
68 changed files with 2488 additions and 640 deletions

View File

@ -105,6 +105,7 @@ Jamey Hicks
Jamie Iles
Jan Van Winkel
Jean Berniolles
Jens Yuechao Liu
Jeremy Bennett
Jesse Taube
Jevin Sweval

View File

@ -719,6 +719,11 @@ Summary:
automatically. Variables explicitly annotated with
:option:`/*verilator&32;split_var*/` are still split.
.. option:: --fslice-element-limit
Rarely needed. Set the maximum array size (number of elements)
for slice optimization to avoid excessive memory usage.
.. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that

View File

@ -41,6 +41,10 @@
#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
# include <cpuid.h> // For __cpuid_count()
#endif
#if defined(__APPLE__) && defined(__MACH__)
# include <mach/mach.h> // For task_info()
#endif
// clang-format on
namespace VlOs {
@ -146,6 +150,15 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
peakr = pmc.PeakWorkingSetSize;
currentr = pmc.WorkingSetSize;
}
#elif defined(__APPLE__) && defined(__MACH__)
mach_task_basic_info_data_t info;
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
const kern_return_t ret
= task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count);
if (ret == KERN_SUCCESS && count == MACH_TASK_BASIC_INFO_COUNT) {
peakr = info.resident_size_max;
currentr = info.resident_size;
}
#else
// Highly unportable. Sorry
std::ifstream is{"/proc/self/status"};

View File

@ -43,6 +43,7 @@ set(HEADERS
V3ActiveTop.h
V3Assert.h
V3AssertPre.h
V3AssertProp.h
V3Ast.h
V3AstAttr.h
V3AstInlines.h
@ -209,6 +210,7 @@ set(COMMON_SOURCES
V3ActiveTop.cpp
V3Assert.cpp
V3AssertPre.cpp
V3AssertProp.cpp
V3Ast.cpp
V3AstNodes.cpp
V3Begin.cpp

View File

@ -232,6 +232,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3ActiveTop.o \
V3Assert.o \
V3AssertPre.o \
V3AssertProp.o \
V3Begin.o \
V3Branch.o \
V3CCtors.o \

View File

@ -143,6 +143,9 @@ class AssertVisitor final : public VNVisitor {
VDouble0 m_statPastVars; // Statistic tracking
bool m_inSampled = false; // True inside a sampled expression
bool m_inRestrict = false; // True inside restrict assertion
AstNode* m_passsp = nullptr; // Current pass statement
AstNode* m_failsp = nullptr; // Current fail statement
bool m_underAssert = false; // Visited from assert
// METHODS
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
@ -288,25 +291,18 @@ class AssertVisitor final : public VNVisitor {
return ifp;
}
static AstNode* assertBody(AstNodeCoverOrAssert* nodep, AstNodeExpr* propp, AstNode* passsp,
AstNode* failsp) {
if (AstPExpr* const pExpr = VN_CAST(propp, PExpr)) {
AstNodeExpr* const condp = pExpr->condp();
UASSERT_OBJ(condp, pExpr, "Should have condition");
AstIf* const ifp = assertCond(nodep, condp->unlinkFrBack(), passsp, failsp);
AstNode* const precondps = pExpr->precondp();
UASSERT_OBJ(precondps, pExpr, "Should have precondition");
precondps->unlinkFrBackWithNext()->addNext(ifp);
AstNodeStmt* const aonp = newIfAssertOn(precondps, nodep->directive(), nodep->type());
FileLine* const flp = precondps->fileline();
AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE};
forkp->addForksp(new AstBegin{flp, "", aonp, true});
return forkp;
AstNode* assertBody(AstNodeCoverOrAssert* nodep, AstNode* propp, AstNode* passsp,
AstNode* failsp) {
AstNode* bodyp = nullptr;
if (AstPExpr* const pexprp = VN_CAST(propp, PExpr)) {
AstFork* const forkp = new AstFork{nodep->fileline(), VJoinType::JOIN_NONE};
forkp->addForksp(pexprp->bodyp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(pexprp), pexprp);
bodyp = forkp;
} else {
bodyp = assertCond(nodep, VN_AS(propp, NodeExpr), passsp, failsp);
}
AstIf* const ifp = assertCond(nodep, propp, passsp, failsp);
return newIfAssertOn(ifp, nodep->directive(), nodep->type());
return newIfAssertOn(bodyp, nodep->directive(), nodep->type());
}
AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
@ -336,7 +332,6 @@ class AssertVisitor final : public VNVisitor {
{ AssertDeFuture{nodep->propp(), m_modp, m_modPastNum++}; }
iterateChildren(nodep);
AstNodeExpr* const propp = VN_AS(nodep->propp()->unlinkFrBackWithNext(), NodeExpr);
AstSenTree* const sentreep = nodep->sentreep();
const string& message = nodep->name();
AstNode* passsp = nodep->passsp();
@ -384,7 +379,15 @@ class AssertVisitor final : public VNVisitor {
nodep->v3fatalSrc("Unknown node type");
}
AstNode* bodysp = assertBody(nodep, propp, passsp, failsp);
VL_RESTORER(m_passsp);
VL_RESTORER(m_failsp);
VL_RESTORER(m_underAssert);
m_passsp = passsp;
m_failsp = failsp;
m_underAssert = true;
iterate(nodep->propp());
AstNode* bodysp = assertBody(nodep, nodep->propp()->unlinkFrBack(), passsp, failsp);
if (sentreep) {
bodysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
}
@ -631,7 +634,7 @@ class AssertVisitor final : public VNVisitor {
}
void visit(AstVarRef* nodep) override {
iterateChildren(nodep);
if (m_inSampled && !VString::startsWith(nodep->name(), "__VpropPrecond")) {
if (m_inSampled && !VString::startsWith(nodep->name(), "__VcycleDly")) {
if (!nodep->access().isReadOnly()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Write to variable in sampled expression");
@ -651,16 +654,27 @@ class AssertVisitor final : public VNVisitor {
m_inSampled = false;
iterateChildren(nodep);
}
void visit(AstPExprClause* nodep) override {
if (m_underAssert) {
if (nodep->pass() && m_passsp) {
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
nodep->replaceWith(m_passsp->cloneTree(true));
} else if (!nodep->pass() && m_failsp) {
// Asserts with multiple statements are wrapped in implicit begin/end blocks so no
// need to clone next.
nodep->replaceWith(m_failsp->cloneTree(false));
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
void visit(AstPExpr* nodep) override {
{
if (m_underAssert) {
VL_RESTORER(m_inSampled);
m_inSampled = false;
iterateAndNextNull(nodep->precondp());
}
iterate(nodep->condp());
if (!m_inRestrict) {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
iterateChildren(nodep);
} else if (m_inRestrict) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
}

View File

@ -56,15 +56,11 @@ private:
AstNodeExpr* m_disablep = nullptr; // Last disable
// Other:
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
V3UniqueNames m_propPrecondNames{"__VpropPrecond"}; // Cycle delay temporaries name generator
bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
AstNodeExpr* m_hasUnsupp = nullptr; // True if assert has unsupported construct inside
AstPExpr* m_pExpr = nullptr; // Current AstPExpr
bool m_hasSExpr = false; // True if assert has AstSExpr inside
bool m_inSExpr = false; // True if in AstSExpr
bool m_inPExpr = false; // True if in AstPExpr
// METHODS
@ -343,7 +339,7 @@ private:
}
AstSenItem* sensesp = nullptr;
if (!m_defaultClockingp) {
if (!m_pExpr && !m_inSExpr) {
if (!m_inPExpr) {
nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
@ -419,33 +415,6 @@ private:
}
}
nodep->user1(true);
} else if (m_inSExpr && !nodep->user1()) {
AstVar* const preVarp
= new AstVar{nodep->varp()->fileline(), VVarType::BLOCKTEMP,
m_propPrecondNames.get(nodep->varp()) + "__" + nodep->varp()->name(),
nodep->varp()->dtypep()};
preVarp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(preVarp);
AstVarRef* const origp
= new AstVarRef{nodep->fileline(), nodep->varp(), VAccess::READ};
origp->user1(true);
AstVarRef* const precondp
= new AstVarRef{preVarp->fileline(), preVarp, VAccess::WRITE};
precondp->user1(true);
// Pack assignments in sampled as in concurrent assertions they are needed.
// Then, in assert's propp, sample only non-precondition variables.
AstSampled* const sampledp = new AstSampled{origp->fileline(), origp};
sampledp->dtypeFrom(origp);
UASSERT(m_pExpr, "Should be under assertion");
AstAssign* const assignp = new AstAssign{m_pExpr->fileline(), precondp, sampledp};
m_pExpr->addPrecondp(assignp);
AstVarRef* const precondReadp
= new AstVarRef{preVarp->fileline(), preVarp, VAccess::READ};
precondReadp->user1(true);
nodep->replaceWith(precondReadp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
void visit(AstMemberSel* nodep) override {
@ -500,32 +469,11 @@ private:
void visit(AstNodeCoverOrAssert* nodep) override {
if (nodep->sentreep()) return; // Already processed
VL_RESTORER(m_hasSExpr);
VL_RESTORER(m_hasUnsupp);
clearAssertInfo();
m_pExpr = new AstPExpr{nodep->propp()->fileline()};
m_pExpr->dtypeFrom(nodep->propp());
// Find Clocking's buried under nodep->exprsp
iterateChildren(nodep);
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
if (m_hasSExpr && m_hasUnsupp) {
if (VN_IS(m_hasUnsupp, Implication)) {
m_hasUnsupp->v3warn(E_UNSUPPORTED,
"Unsupported: Implication with sequence expression");
} else {
m_hasUnsupp->v3warn(E_UNSUPPORTED,
"Unsupported: Disable iff with sequence expression");
}
if (m_pExpr) VL_DO_DANGLING(pushDeletep(m_pExpr), m_pExpr);
} else if (m_pExpr && m_pExpr->precondp()) {
m_pExpr->condp(VN_AS(nodep->propp()->unlinkFrBackWithNext(), NodeExpr));
nodep->propp(m_pExpr);
iterateAndNextNull(m_pExpr->precondp());
} else if (m_pExpr) {
VL_DO_DANGLING(pushDeletep(m_pExpr), m_pExpr);
}
clearAssertInfo();
}
void visit(AstFalling* nodep) override {
@ -564,10 +512,6 @@ private:
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
nodep->sentreep(newSenTree(nodep));
}
void visit(AstLogNot* nodep) override {
if (m_inSExpr) nodep->v3error("Syntax error: unexpected 'not' in sequence expression");
iterateChildren(nodep);
}
void visit(AstPast* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
@ -633,7 +577,6 @@ private:
void visit(AstImplication* nodep) override {
if (nodep->sentreep()) return; // Already processed
m_hasUnsupp = nodep;
iterateChildren(nodep);
@ -685,7 +628,6 @@ private:
}
if (AstNodeExpr* const disablep = nodep->disablep()) {
m_disablep = disablep;
m_hasUnsupp = disablep;
if (VN_IS(nodep->backp(), Cover)) {
blockp = new AstAnd{disablep->fileline(),
new AstNot{disablep->fileline(), disablep->unlinkFrBack()},
@ -699,17 +641,19 @@ private:
nodep->replaceWith(blockp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstSExpr* nodep) override {
VL_RESTORER(m_inSExpr);
m_inSExpr = true;
m_hasSExpr = true;
void visit(AstPExpr* nodep) override {
VL_RESTORER(m_inPExpr);
m_inPExpr = true;
UASSERT_OBJ(m_pExpr, nodep, "Should be under assertion");
m_pExpr->addPrecondp(nodep->delayp()->unlinkFrBack());
if (AstLogNot* const notp = VN_CAST(nodep->backp(), LogNot)) {
notp->replaceWith(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(notp), notp);
iterate(nodep);
} else {
iterateChildren(nodep);
}
iterateChildren(nodep);
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_defaultClockingp);

264
src/V3AssertProp.cpp Normal file
View File

@ -0,0 +1,264 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Implementation of assertion properties
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// Each sequence is translated into a decision tree in form of deterministic
// finite automaton (DFA) with bipartite structure. Each cycle delay is connected
// with an expression that depending on an evaluation result, proceeds to the next
// evaluation state. The structure is rooted with original sequence expression for
// simplifying further transformation back to AST.
//
// The graph consists of the following nodes:
//
// DfaStmtVertex: Statements to be executed to traverse from one state to another
// DfaExprVertex: Property expression that is checked and based on that a branch
// is taken.
// DfaConditionEdge: Branch edge that connects statements and expressions.
//
// Properties steps:
// Ensemble a property decision tree from sequence expressions.
// Transform property decision tree into AST, remove source sequence expression
// Property blocks are wrapped with AstPExpr that are transformed
// further by V3AssertPre and V3Assert.
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3AssertProp.h"
#include "V3Graph.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Data structures (graph types)
class DfaVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(DfaVertex, V3GraphVertex)
// STATE
AstNode* const m_nodep; // Underlying node
public:
// CONSTRUCTORS
explicit DfaVertex(V3Graph* graphp, AstNode* nodep) VL_MT_DISABLED : V3GraphVertex{graphp},
m_nodep{nodep} {}
AstNode* nodep() const { return m_nodep; }
string name() const override VL_MT_STABLE {
return cvtToHex(m_nodep) + "\\n " + cvtToStr(m_nodep->typeName()) + "\\n"s
+ m_nodep->fileline()->ascii();
};
string dotShape() const override {
if (inEmpty()) return "tripleoctagon";
if (outEmpty()) return "doubleoctagon";
return "oval";
}
bool isStart() const { return inEmpty(); }
};
class DfaStmtVertex final : public DfaVertex {
VL_RTTI_IMPL(DfaStmtVertex, V3GraphEdge)
public:
// CONSTRUCTORS
explicit DfaStmtVertex(V3Graph* graphp, AstNodeStmt* stmtp) VL_MT_DISABLED
: DfaVertex{graphp, stmtp} {}
string dotColor() const override { return "green"; }
};
class DfaExprVertex final : public DfaVertex {
VL_RTTI_IMPL(DfaExprVertex, V3GraphEdge)
public:
// CONSTRUCTORS
explicit DfaExprVertex(V3Graph* graphp, AstNodeExpr* exprp) VL_MT_DISABLED
: DfaVertex{graphp, exprp} {}
string dotColor() const override { return "blue"; }
};
class DfaConditionEdge final : public V3GraphEdge {
VL_RTTI_IMPL(DfaConditionEdge, V3GraphEdge)
// STATE
const bool m_ifBranch; // Whether this branch is taken for fulfilled condition
public:
// CONSTRUCTORS
explicit DfaConditionEdge(V3Graph* graphp, DfaVertex* fromp, DfaVertex* top,
bool ifBranch) VL_MT_DISABLED : V3GraphEdge{graphp, fromp, top, 1},
m_ifBranch{ifBranch} {}
~DfaConditionEdge() override = default;
bool ifBranch() const { return m_ifBranch; }
string dotColor() const override { return m_ifBranch ? "green" : "red"; }
};
// Parse properties and ensemble a property tree graph
class AssertPropBuildVisitor final : public VNVisitorConst {
// STATE
V3Graph& m_graph; // Property tree
DfaVertex* m_lastVtxp = nullptr; // Last encountered vertex
bool m_underSExpr = false; // Is under sequence expression, for creating a start node
size_t m_underLogNots = 0; // Number of 'not' operators before sequence
DfaStmtVertex* makeClause(AstSExpr* nodep, bool pass) {
return new DfaStmtVertex{
&m_graph,
new AstPExprClause{nodep->fileline(), m_underLogNots % 2 == 0 ? pass : !pass}};
}
// VISITORS
void visit(AstNodeCoverOrAssert* nodep) override { iterateChildrenConst(nodep); }
void visit(AstLogNot* nodep) override {
VL_RESTORER(m_underLogNots);
++m_underLogNots;
iterateChildrenConst(nodep);
}
void visit(AstSExpr* nodep) override {
if (VN_IS(nodep->exprp(), SExpr)) {
VL_RESTORER(m_underSExpr);
m_underSExpr = true;
iterateConst(nodep->exprp());
} else {
DfaExprVertex* const exprVtxp
= new DfaExprVertex{&m_graph, nodep->exprp()->unlinkFrBack()};
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, true), true};
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
m_lastVtxp = exprVtxp;
}
DfaExprVertex* const startVtxp
= m_underSExpr ? nullptr : new DfaExprVertex{&m_graph, nodep};
DfaStmtVertex* const dlyVtxp
= new DfaStmtVertex{&m_graph, nodep->delayp()->unlinkFrBack()};
if (AstSExpr* const sexprp = VN_CAST(nodep->preExprp(), SExpr)) {
UASSERT_OBJ(!sexprp->preExprp() && !VN_IS(sexprp->exprp(), SExpr), sexprp,
"Incorrect sexpr tree");
DfaStmtVertex* const sdlyVtxp
= new DfaStmtVertex{&m_graph, sexprp->delayp()->unlinkFrBack()};
DfaExprVertex* const exprVtxp
= new DfaExprVertex{&m_graph, sexprp->exprp()->unlinkFrBack()};
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, sdlyVtxp, true};
new DfaConditionEdge{&m_graph, sdlyVtxp, exprVtxp, true};
new DfaConditionEdge{&m_graph, exprVtxp, dlyVtxp, true};
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
// This case only occurs when multi-delay sequence starts with an expression,
// don't set last as this is never a last expression.
} else if (nodep->preExprp()) {
DfaExprVertex* const preVtxp
= new DfaExprVertex{&m_graph, nodep->preExprp()->unlinkFrBack()};
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, preVtxp, true};
new DfaConditionEdge{&m_graph, preVtxp, dlyVtxp, true};
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
new DfaConditionEdge{&m_graph, preVtxp, makeClause(nodep, false), false};
m_lastVtxp = preVtxp;
} else {
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, dlyVtxp, true};
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
m_lastVtxp = dlyVtxp;
}
}
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
void visit(AstConstPool* nodep) override {}
public:
// CONSTRUCTORS
explicit AssertPropBuildVisitor(AstNetlist* nodep, V3Graph& graph)
: m_graph{graph} {
iterateConst(nodep);
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixedAlways("properties", true);
}
~AssertPropBuildVisitor() override = default;
};
// Transform property graph into AST
class AssertPropTransformer final {
// STATE
V3Graph& m_graph; // Property tree
AstPExpr* m_pexprp = nullptr; // Currently built property sequence
AstBegin* m_current = nullptr; // Currently built block
V3GraphVertex* processVtx(V3GraphVertex* vtxp) {
if (DfaStmtVertex* const stmtp = vtxp->cast<DfaStmtVertex>()) return processVtx(stmtp);
if (DfaExprVertex* const exprp = vtxp->cast<DfaExprVertex>()) return processVtx(exprp);
// TODO use C++17 std::variant and std::visit
v3fatalSrc("Unexpected vertex type");
return nullptr;
}
V3GraphVertex* processVtx(DfaStmtVertex* vtxp) {
UASSERT_OBJ(!vtxp->isStart(), vtxp->nodep(),
"Starting node should be a property expression");
UASSERT_OBJ(m_current, vtxp->nodep(), "Should be under a block");
m_current->addStmtsp(vtxp->nodep());
return processEdge(vtxp->outEdges().frontp());
}
V3GraphVertex* processVtx(DfaExprVertex* vtxp) {
AstNode* const nodep = vtxp->nodep();
if (vtxp->isStart()) {
AstBegin* const bodyp = new AstBegin{nodep->fileline(), "", nullptr, true};
m_pexprp = new AstPExpr{nodep->fileline(), bodyp, nodep->dtypep()};
UASSERT_OBJ(vtxp->outSize1(), nodep, "Starting node must have one out edge");
m_current = m_pexprp->bodyp();
return processEdge(vtxp->outEdges().frontp());
}
UASSERT_OBJ(vtxp->outEdges().size() == 2, nodep, "Each expression must have two branches");
AstBegin* const passsp = new AstBegin{nodep->fileline(), "", nullptr, true};
AstNode* const failsp = vtxp->outEdges().backp()->top()->as<DfaStmtVertex>()->nodep();
AstSampled* const sampledp
= new AstSampled{nodep->fileline(), VN_AS(vtxp->nodep(), NodeExpr)};
sampledp->dtypeFrom(vtxp->nodep());
AstIf* const ifp = new AstIf{nodep->fileline(), sampledp, passsp, failsp};
m_current->addStmtsp(ifp);
m_current = passsp;
return processEdge(vtxp->outEdges().frontp());
}
V3GraphVertex* processEdge(V3GraphEdge* edgep) {
if (edgep) return processVtx(edgep->top());
return nullptr;
}
public:
// CONSTRUCTORS
explicit AssertPropTransformer(V3Graph& graph)
: m_graph{graph} {
for (V3GraphVertex& vtx : m_graph.vertices()) {
if (DfaVertex* const dVtxp = vtx.cast<DfaExprVertex>()) {
if (dVtxp->isStart()) {
VL_RESTORER(m_pexprp);
processVtx(&vtx);
AstSExpr* const propp = VN_AS(dVtxp->nodep(), SExpr);
propp->replaceWith(m_pexprp);
VL_DO_DANGLING(propp->deleteTree(), propp);
}
}
}
}
};
//######################################################################
// Top AssertProp class
void V3AssertProp::assertPropAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{
V3Graph graph;
{ AssertPropBuildVisitor{nodep, graph}; }
AssertPropTransformer{graph};
}
V3Global::dumpCheckGlobalTree("assertproperties", 0, dumpTreeEitherLevel() >= 3);
}

32
src/V3AssertProp.h Normal file
View File

@ -0,0 +1,32 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Implementation of assertion properties
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3ASSERTPROP_H_
#define VERILATOR_V3ASSERTPROP_H_
#include "config_build.h"
#include "verilatedos.h"
class AstNetlist;
//============================================================================
class V3AssertProp final {
public:
static void assertPropAll(AstNetlist* nodep) VL_MT_DISABLED;
};
#endif // Guard

View File

@ -613,11 +613,14 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
backp->m_nextp = nullptr;
// Old list gets truncated
// New list becomes a list upon itself
// Most common case is unlinking a entire operand tree
// (else we'd probably call unlinkFrBack without next)
// We may be in the middle of a list; we have no way to find head or tail!
AstNode* oldtailp = oldp;
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
// Most common case is unlinking a entire operand tree, or all but the
// head (else we'd probably call unlinkFrBack without next)
AstNode* oldtailp = backp->m_headtailp;
if (!oldtailp) {
// We are in the middle of a list; we have no way to find head or tail in O(1)
oldtailp = oldp;
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
}
// Create new head/tail of old list
AstNode* const oldheadp = oldtailp->m_headtailp;
oldheadp->m_headtailp = oldp->m_backp;

View File

@ -427,7 +427,7 @@ class AstNode VL_NOT_FINAL {
#endif
AstNodeDType* m_dtypep = nullptr; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list, else nullptr
FileLine* m_fileline; // Where it was declared
#ifdef VL_DEBUG
// Only keep track of the edit count in the node in the debug build.
@ -902,6 +902,8 @@ public:
// isUnlikely handles $stop or similar statement which means an above IF
// statement is unlikely to be taken
virtual bool isUnlikely() const { return false; }
// Is an IEEE system function (versus internally-generated)
virtual bool isSystemFunc() const { return false; }
virtual int instrCount() const { return 0; }
// Iff node is identical to another node
virtual bool isSame(const AstNode* samep) const {

View File

@ -134,6 +134,7 @@ public:
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; }
};
@ -172,6 +173,7 @@ public:
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
bool doubleFlavor() const override { return true; }
};
@ -347,6 +349,7 @@ protected:
public:
ASTGEN_MEMBERS_AstNodeTermop;
bool isSystemFunc() const override { return true; }
};
class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr {
// Ternary expression
@ -403,6 +406,7 @@ public:
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool sizeMattersThs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
const V3Number& ths) override {
@ -449,6 +453,7 @@ public:
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_DBL_TRIG; }
bool doubleFlavor() const override { return true; }
};
@ -1350,6 +1355,7 @@ public:
int instrCount() const override { return widthInstrs() * 64; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
bool isSystemFunc() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstFOpen final : public AstNodeExpr {
@ -1372,6 +1378,7 @@ public:
bool isOutputter() override { return true; }
bool isUnlikely() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFOpenMcd final : public AstNodeExpr {
// @astgen op2 := filenamep : AstNodeExpr
@ -1415,6 +1422,7 @@ public:
bool isOutputter() override { return true; } // SPECIAL: makes output
bool cleanOut() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFRewind final : public AstNodeExpr {
// @astgen op1 := filep : Optional[AstNode]
@ -1462,6 +1470,7 @@ public:
}
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text = text; }
bool isSystemFunc() const override { return true; }
};
class AstFSeek final : public AstNodeExpr {
// @astgen op1 := filep : AstNode // file (must be a VarRef)
@ -1484,6 +1493,7 @@ public:
bool isOutputter() override { return true; } // SPECIAL: makes output
bool cleanOut() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFTell final : public AstNodeExpr {
// @astgen op1 := filep : AstNode // file (must be a VarRef)
@ -1503,6 +1513,7 @@ public:
bool isUnlikely() const override { return true; }
bool cleanOut() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFalling final : public AstNodeExpr {
// Verilog $falling_gclk
@ -1519,6 +1530,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFell final : public AstNodeExpr {
// Verilog $fell
@ -1537,6 +1549,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstFuture final : public AstNodeExpr {
// Verilog $future_gclk
@ -1555,6 +1568,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstGatePin final : public AstNodeExpr {
// Possibly expand a gate primitive input pin value to match the range of the gate primitive
@ -1764,11 +1778,13 @@ public:
};
class AstPExpr final : public AstNodeExpr {
// Property expression
// @astgen op1 := precondp : List[AstNode]
// @astgen op2 := condp : Optional[AstNodeExpr]
// @astgen op1 := bodyp : AstBegin
public:
explicit AstPExpr(FileLine* fl)
: ASTGEN_SUPER_PExpr(fl) {}
explicit AstPExpr(FileLine* fl, AstBegin* bodyp, AstNodeDType* dtypep)
: ASTGEN_SUPER_PExpr(fl) {
this->bodyp(bodyp);
this->dtypep(dtypep);
}
ASTGEN_MEMBERS_AstPExpr;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
@ -1835,6 +1851,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstPatMember final : public AstNodeExpr {
// Verilog '{a} or '{a{b}}
@ -1923,6 +1940,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return !seedp(); }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_PLI; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool combinable(const AstRand* samep) const {
@ -1947,6 +1965,7 @@ public:
bool cleanOut() const override { return false; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_PLI; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
@ -1965,6 +1984,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstRose final : public AstNodeExpr {
// Verilog $rose
@ -1983,14 +2003,23 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstSExpr final : public AstNodeExpr {
// Sequence expression
// @astgen op1 := delayp : AstDelay
// @astgen op2 := exprp : AstNodeExpr
// @astgen op1 := preExprp: Optional[AstNodeExpr]
// @astgen op2 := delayp : AstNodeStmt<AstDelay|AstBegin>
// @astgen op3 := exprp : AstNodeExpr
public:
explicit AstSExpr(FileLine* fl, AstDelay* delayp, AstNodeExpr* exprp)
explicit AstSExpr(FileLine* fl, AstNodeExpr* preExprp, AstNodeStmt* delayp, AstNodeExpr* exprp)
: ASTGEN_SUPER_SExpr(fl) {
this->preExprp(preExprp);
this->delayp(delayp);
this->exprp(exprp);
}
explicit AstSExpr(FileLine* fl, AstNodeStmt* delayp, AstNodeExpr* exprp)
: ASTGEN_SUPER_SExpr(fl) {
this->preExprp(nullptr);
this->delayp(delayp);
this->exprp(exprp);
}
@ -2055,6 +2084,7 @@ public:
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
bool isSystemFunc() const override { return true; }
};
class AstSScanF final : public AstNodeExpr {
// @astgen op1 := exprsp : List[AstNode] // VarRefs for results
@ -2086,6 +2116,7 @@ public:
string text() const { return m_text; } // * = Text to display
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
bool isSystemFunc() const override { return true; }
};
class AstSampled final : public AstNodeExpr {
// Verilog $sampled
@ -2102,6 +2133,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return 0; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstScopeName final : public AstNodeExpr {
// For display %m and DPI context imports
@ -2228,6 +2260,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstStackTraceF final : public AstNodeExpr {
// $stacktrace used as function
@ -2263,6 +2296,7 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstStructSel final : public AstNodeExpr {
// Unpacked struct/union member access
@ -2330,6 +2364,7 @@ public:
bool isUnlikely() const override { return true; }
bool cleanOut() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isSystemFunc() const override { return true; }
};
class AstTestPlusArgs final : public AstNodeExpr {
// Search expression. If nullptr then this is a $test$plusargs instead of $value$plusargs.
@ -2346,6 +2381,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
// but isPure() true
bool isSystemFunc() const override { return true; }
bool cleanOut() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
@ -2380,6 +2416,7 @@ public:
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
@ -2396,6 +2433,7 @@ public:
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
VTimescale timeunit() const { return m_timeunit; }
@ -2450,6 +2488,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return !outp(); }
bool isSystemFunc() const override { return true; }
bool cleanOut() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
@ -2553,6 +2592,7 @@ public:
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs() * 20; }
bool isPure() override { return false; }
};
@ -2731,6 +2771,7 @@ public:
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs() * 64; }
AstNode* strgp() const { return lhsp(); }
AstNode* filep() const { return rhsp(); }
@ -2751,6 +2792,7 @@ public:
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs() * 64; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
@ -3667,6 +3709,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_PLI; }
};
@ -4452,6 +4495,7 @@ public:
bool sizeMattersRhs() const override { return false; }
bool sizeMattersThs() const override { return false; }
bool sizeMattersFhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs() * 16; }
};
@ -4466,6 +4510,7 @@ public:
string emitVerilog() override { return "%f$inferred_disable"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isSystemFunc() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstTime final : public AstNodeTermop {
@ -4482,6 +4527,7 @@ public:
bool cleanOut() const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_TIME; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
void dump(std::ostream& str = std::cout) const override;
@ -4503,6 +4549,7 @@ public:
bool cleanOut() const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return INSTR_COUNT_TIME; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
void dump(std::ostream& str = std::cout) const override;
@ -4795,6 +4842,7 @@ public:
bool cleanLhs() const override { return false; } // Eliminated before matters
bool sizeMattersLhs() const override { return false; } // Eliminated before matters
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstCAwait final : public AstNodeUniop {
// Emit C++'s co_await expression
@ -4867,6 +4915,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 16; }
bool isSystemFunc() const override { return true; }
};
class AstCastWrap final : public AstNodeUniop {
// A cast which has been expanded and the LHSP does all the lifting
@ -4896,6 +4945,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 16; }
bool isSystemFunc() const override { return true; }
};
class AstCvtPackString final : public AstNodeUniop {
// Convert to Verilator Packed String (aka verilog "string")
@ -4977,6 +5027,7 @@ public:
int instrCount() const override { return widthInstrs() * 16; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
bool isSystemFunc() const override { return true; }
AstNode* filep() const { return lhsp(); }
};
class AstFGetC final : public AstNodeUniop {
@ -4995,6 +5046,7 @@ public:
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
AstNode* filep() const { return lhsp(); }
bool isSystemFunc() const override { return true; }
};
class AstISToRD final : public AstNodeUniop {
// $itor where lhs is signed
@ -5012,6 +5064,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstIToRD final : public AstNodeUniop {
// $itor where lhs is unsigned
@ -5028,6 +5081,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstIsUnbounded final : public AstNodeUniop {
// True if is unbounded ($)
@ -5046,6 +5100,7 @@ public:
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool isSystemFunc() const override { return true; }
};
class AstIsUnknown final : public AstNodeUniop {
// True if any unknown bits
@ -5061,6 +5116,7 @@ public:
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool isSystemFunc() const override { return true; }
};
class AstLenN final : public AstNodeUniop {
// Length of a string
@ -5191,6 +5247,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 4; }
bool isSystemFunc() const override { return true; }
};
class AstOneHot0 final : public AstNodeUniop {
// True if only single bit, or no bits set in vector
@ -5207,6 +5264,7 @@ public:
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 3; }
bool isSystemFunc() const override { return true; }
};
class AstRToIRoundS final : public AstNodeUniop {
// Convert real to integer, with arbitrary sized output (not just "integer" format)
@ -5225,6 +5283,7 @@ public:
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstRToIS final : public AstNodeUniop {
// $rtoi(lhs)
@ -5241,6 +5300,7 @@ public:
bool cleanLhs() const override { return false; } // Eliminated before matters
bool sizeMattersLhs() const override { return false; } // Eliminated before matters
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstRealToBits final : public AstNodeUniop {
public:
@ -5256,6 +5316,7 @@ public:
bool cleanLhs() const override { return false; } // Eliminated before matters
bool sizeMattersLhs() const override { return false; } // Eliminated before matters
int instrCount() const override { return INSTR_COUNT_DBL; }
bool isSystemFunc() const override { return true; }
};
class AstRedAnd final : public AstNodeUniop {
public:
@ -5337,6 +5398,7 @@ public:
bool cleanLhs() const override { return false; } // Eliminated before matters
bool sizeMattersLhs() const override { return true; } // Eliminated before matters
int instrCount() const override { return 0; }
bool isSystemFunc() const override { return true; }
};
class AstTimeImport final : public AstNodeUniop {
// Take a constant that represents a time and needs conversion based on time units
@ -5419,6 +5481,7 @@ public:
bool cleanLhs() const override { return false; } // Eliminated before matters
bool sizeMattersLhs() const override { return true; } // Eliminated before matters
int instrCount() const override { return 0; }
bool isSystemFunc() const override { return true; }
};
// === AstNodeSystemUniopD ===

View File

@ -941,6 +941,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; } // * = Scope name
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
@ -1871,7 +1872,7 @@ class AstVar final : public AstNode {
bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
void init() {
m_ansi = false;
m_declTyped = false;
@ -1921,6 +1922,7 @@ class AstVar final : public AstNode {
m_ignorePostWrite = false;
m_ignoreSchedWrite = false;
m_dfgMultidriven = false;
m_globalConstrained = false;
}
public:
@ -2085,7 +2087,8 @@ public:
void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; }
bool dfgMultidriven() const { return m_dfgMultidriven; }
void setDfgMultidriven() { m_dfgMultidriven = true; }
void globalConstrained(bool flag) { m_globalConstrained = flag; }
bool globalConstrained() const { return m_globalConstrained; }
// METHODS
void name(const string& name) override { m_name = name; }
void tag(const string& text) override { m_tag = text; }

View File

@ -821,6 +821,17 @@ public:
}
bool off() const { return m_off; }
};
class AstPExprClause final : public AstNodeStmt {
const bool m_pass; // True if will be replaced by passing assertion clause, false for
// assertion failure clause
public:
ASTGEN_MEMBERS_AstPExprClause;
explicit AstPExprClause(FileLine* fl, bool pass = true)
: ASTGEN_SUPER_PExprClause(fl)
, m_pass{pass} {}
bool pass() const { return m_pass; }
};
class AstPrintTimeScale final : public AstNodeStmt {
// Parents: stmtlist
string m_name; // Parent module name

View File

@ -24,6 +24,7 @@
#include "V3Clock.h"
#include "V3Const.h"
#include "V3Sched.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@ -62,7 +63,7 @@ class ClockVisitor final : public VNVisitor {
// NODE STATE
// STATE
AstCFunc* const m_evalp = nullptr; // The '_eval' function
AstCFunc* m_sampleCFuncp = nullptr; // The CFunc to populate with sampled value assignments
// VISITORS
void visit(AstCoverToggle* nodep) override {
@ -98,19 +99,20 @@ class ClockVisitor final : public VNVisitor {
//========== Move sampled assignments
void visit(AstVarScope* nodep) override {
AstVar* const varp = nodep->varp();
if (varp->valuep() && varp->name().substr(0, strlen("__Vsampled")) == "__Vsampled") {
FileLine* const flp = nodep->fileline();
AstNodeExpr* const rhsp = VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr);
AstVarRef* const lhsp = new AstVarRef{flp, nodep, VAccess::WRITE};
AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp};
if (AstNode* const stmtsp = m_evalp->stmtsp()) {
stmtsp->addHereThisAsNext(assignp);
} else {
m_evalp->addStmtsp(assignp);
}
varp->direction(VDirection::NONE); // Restore defaults
varp->primaryIO(false);
if (!varp->valuep()) return;
if (!VString::startsWith(varp->name(), "__Vsampled")) return;
// Create the containing function on first encounter
if (!m_sampleCFuncp) {
m_sampleCFuncp = V3Sched::util::makeSubFunction(v3Global.rootp(), "_sample", false);
}
FileLine* const flp = nodep->fileline();
AstNodeExpr* const rhsp = VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr);
AstVarRef* const lhsp = new AstVarRef{flp, nodep, VAccess::WRITE};
m_sampleCFuncp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
varp->direction(VDirection::NONE); // Restore defaults
varp->primaryIO(false);
}
//--------------------
@ -118,9 +120,15 @@ class ClockVisitor final : public VNVisitor {
public:
// CONSTRUCTORS
explicit ClockVisitor(AstNetlist* netlistp)
: m_evalp{netlistp->evalp()} {
explicit ClockVisitor(AstNetlist* netlistp) {
iterate(netlistp);
// If we need a sample function, call it at the begining of eval
if (m_sampleCFuncp) {
V3Sched::util::splitCheck(m_sampleCFuncp);
AstCCall* const callp = new AstCCall{m_sampleCFuncp->fileline(), m_sampleCFuncp};
callp->dtypeSetVoid();
netlistp->evalp()->stmtsp()->addHereThisAsNext(callp->makeStmt());
}
}
~ClockVisitor() override = default;
};

View File

@ -141,7 +141,7 @@ class CoverageVisitor final : public VNVisitor {
const VNUser2InUse m_inuser2;
V3UniqueNames m_exprTempNames; // For generating unique temporary variable names used by
// expression coverage
std::unordered_map<VNRef<AstFuncRef>, AstVar*> m_funcTemps;
std::unordered_map<AstNodeExpr*, AstVar*> m_funcTemps;
// STATE - across all visitors
int m_nextHandle = 0;
@ -277,6 +277,7 @@ class CoverageVisitor final : public VNVisitor {
VL_RESTORER(m_modp);
VL_RESTORER(m_state);
VL_RESTORER(m_exprTempNames);
VL_RESTORER(m_funcTemps);
createHandle(nodep);
m_modp = nodep;
m_state.m_inModOff
@ -333,6 +334,7 @@ class CoverageVisitor final : public VNVisitor {
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
VL_RESTORER(m_exprTempNames);
VL_RESTORER(m_funcTemps);
m_ftaskp = nodep;
if (!nodep->dpiImport()) iterateProcedure(nodep);
}
@ -784,9 +786,10 @@ class CoverageVisitor final : public VNVisitor {
comment += (first ? "" : " && ") + term.m_emitV
+ "==" + (term.m_objective ? "1" : "0");
AstNodeExpr* covExprp = nullptr;
if (AstFuncRef* const frefp = VN_CAST(term.m_exprp, FuncRef)) {
AstNodeDType* const dtypep = frefp->taskp()->fvarp()->dtypep();
const auto pair = m_funcTemps.emplace(*frefp, nullptr);
if (VN_IS(term.m_exprp, FuncRef) || term.m_exprp->isSystemFunc()) {
AstNodeExpr* const frefp = term.m_exprp;
AstNodeDType* const dtypep = frefp->dtypep();
const auto pair = m_funcTemps.emplace(frefp, nullptr);
AstVar* varp = pair.first->second;
if (pair.second) {
varp = new AstVar{fl, VVarType::MODULETEMP, m_exprTempNames.get(frefp),

View File

@ -105,7 +105,7 @@ class EmitCSyms final : EmitCBaseVisitorConst {
size_t m_funcNum = 0; // CFunc split function number
V3OutCFile* m_ofpBase = nullptr; // Base (not split) C file
AstCFile* m_ofpBaseFile = nullptr; // Base (not split) AstCFile
std::unordered_map<int, bool> m_usesVfinal; // Split method uses __Vfinal
std::vector<std::pair<size_t, bool>> m_usesVfinal; // Split file index + uses __Vfinal
VDouble0 m_statVarScopeBytes; // Statistic tracking
const std::string m_symsFileBase = v3Global.opt.makeDir() + "/" + symClassName();
@ -620,7 +620,7 @@ void EmitCSyms::checkSplit(bool usesVfinal) {
puts(");\n");
// Create new split file
m_usesVfinal[funcNum] = usesVfinal;
m_usesVfinal.emplace_back(funcNum, usesVfinal);
const std::string filename = m_symsFileBase + "__" + std::to_string(funcNum) + ".cpp";
AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/);
cfilep->support(true);

View File

@ -365,6 +365,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
void visit(AstSFormat* nodep) override {
visitNodeDisplay(nodep, nodep->lhsp(), nodep->fmtp()->text(), nodep->fmtp()->exprsp());
}
void visit(AstToStringN* nodep) override { iterateConst(nodep->lhsp()); }
void visit(AstSFormatF* nodep) override {
visitNodeDisplay(nodep, nullptr, nodep->text(), nodep->exprsp());
}
@ -699,6 +700,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(),
nodep->thsp());
}
void visit(AstNodeQuadop* nodep) override {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(), nodep->thsp(),
nodep->fhsp());
}
void visit(AstMemberSel* nodep) override {
iterateConst(nodep->fromp());
puts(".");
@ -799,9 +804,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
puts(";\n");
}
void visit(AstNodeCoverOrAssert* nodep) override {
if (AstPExpr* const pexprp = VN_CAST(nodep->propp(), PExpr)) {
iterateAndNextConstNull(pexprp);
}
putfs(nodep, nodep->verilogKwd() + " ");
if (nodep->type() == VAssertType::OBSERVED_DEFERRED_IMMEDIATE) {
puts("#0 ");
@ -812,14 +814,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
iterateConstNull(nodep->sentreep());
puts("(");
if (const AstPExpr* const pexprp = VN_CAST(nodep->propp(), PExpr)) {
iterateAndNextConstNull(pexprp->condp());
if (AstSampled* const sampledp = VN_CAST(nodep->propp(), Sampled)) {
iterateAndNextConstNull(sampledp->exprp());
} else {
if (AstSampled* const sampledp = VN_CAST(nodep->propp(), Sampled)) {
iterateAndNextConstNull(sampledp->exprp());
} else {
iterateAndNextConstNull(nodep->propp());
}
iterateAndNextConstNull(nodep->propp());
}
if (!VN_IS(nodep, Restrict)) {
puts(") begin\n");
@ -1052,16 +1050,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateConstNull(nodep->propp());
puts("\n");
}
void visit(AstPExpr* nodep) override {
iterateAndNextConstNull(nodep->precondp()); // condp emitted by AstNodeCoverOrAssert
}
void visit(AstPExpr* nodep) override { iterateConst(nodep->bodyp()); }
void visit(AstSExpr* nodep) override {
iterateConstNull(nodep->preExprp());
{
VL_RESTORER(m_suppressSemi);
m_suppressSemi = true;
iterateConstNull(nodep->delayp());
iterateConst(nodep->delayp());
}
iterateConstNull(nodep->exprp());
iterateConst(nodep->exprp());
}
// Terminals

View File

@ -293,29 +293,27 @@ class ForceConvertVisitor final : public VNVisitor {
AstAssign* const resetRdp
= new AstAssign{flp, lhsp->cloneTreePure(false), lhsp->unlinkFrBack()};
// Replace write refs on the LHS
resetRdp->lhsp()->foreach([this](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
AstVarScope* const newVscp = vscp->varp()->isContinuously()
? m_state.getForceComponents(vscp).m_rdVscp
: vscp;
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::WRITE};
refp->replaceWith(newpRefp);
VL_DO_DANGLING(refp->deleteTree(), refp);
});
// Replace write refs on RHS
resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) {
resetRdp->lhsp()->foreach([this](AstVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
if (vscp->varp()->isContinuously()) {
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ};
ForceState::markNonReplaceable(newpRefp);
AstVarRef* const newpRefp = new AstVarRef{
refp->fileline(), m_state.getForceComponents(vscp).m_rdVscp, VAccess::WRITE};
refp->replaceWith(newpRefp);
VL_DO_DANGLING(refp->deleteTree(), refp);
}
});
// Replace write refs on RHS
resetRdp->rhsp()->foreach([this](AstVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
if (vscp->varp()->isContinuously()) {
refp->access(VAccess::READ);
ForceState::markNonReplaceable(refp);
} else {
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp));
VL_DO_DANGLING(refp->deleteTree(), refp);
}
VL_DO_DANGLING(refp->deleteTree(), refp);
});
resetRdp->addNext(resetEnp);

View File

@ -1458,13 +1458,16 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-freloop", FOnOff, &m_fReloop);
DECL_OPTION("-freorder", FOnOff, &m_fReorder);
DECL_OPTION("-fslice", FOnOff, &m_fSlice);
DECL_OPTION("-fslice-element-limit", CbVal, [this, fl](const char* valp) {
m_fSliceElementLimit = std::atoi(valp);
if (m_fSliceElementLimit < 0) fl->v3fatal("--fslice-element-limit must be >= 0: " << valp);
});
DECL_OPTION("-fsplit", FOnOff, &m_fSplit);
DECL_OPTION("-fsubst", FOnOff, &m_fSubst);
DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst);
DECL_OPTION("-ftable", FOnOff, &m_fTable);
DECL_OPTION("-ftaskify-all-forked", FOnOff, &m_fTaskifyAll).undocumented(); // Debug
DECL_OPTION("-fvar-split", FOnOff, &m_fVarSplit);
DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
DECL_OPTION("-gdb", CbCall, []() {}); // Processed only in bin/verilator shell

View File

@ -415,6 +415,7 @@ private:
bool m_fReloop; // main switch: -fno-reloop: reform loops
bool m_fReorder; // main switch: -fno-reorder: reorder assignments in blocks
bool m_fSlice = true; // main switch: -fno-slice: array assignment slicing
int m_fSliceElementLimit = 256; // main switch: --fslice-element-limit
bool m_fSplit; // main switch: -fno-split: always assignment splitting
bool m_fSubst; // main switch: -fno-subst: substitute expression temp values
bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution
@ -730,6 +731,7 @@ public:
bool fReloop() const { return m_fReloop; }
bool fReorder() const { return m_fReorder; }
bool fSlice() const { return m_fSlice; }
int fSliceElementLimit() const { return m_fSliceElementLimit; }
bool fSplit() const { return m_fSplit; }
bool fSubst() const { return m_fSubst; }
bool fSubstConst() const { return m_fSubstConst; }

View File

@ -54,10 +54,17 @@ VL_DEFINE_DEBUG_FUNCTIONS;
enum ClassRandom : uint8_t {
NONE, // randomize() is not called
IS_RANDOMIZED, // randomize() is called
IS_RANDOMIZED_GLOBAL, // randomize() is called with global constraints
IS_RANDOMIZED_INLINE, // randomize() with args is called
IS_STD_RANDOMIZED, // std::randomize() is called
};
// ######################################################################
// Constants for global constraint processing
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__VBasicRand";
// ######################################################################
// Establishes the target of a rand_mode() call
@ -138,6 +145,9 @@ class RandomizeMarkVisitor final : public VNVisitor {
AstNodeModule* m_modp; // Current module
AstNodeStmt* m_stmtp = nullptr; // Current statement
std::set<AstNodeVarRef*> m_staticRefs; // References to static variables under `with` clauses
AstWith* m_withp = nullptr; // Current 'with' constraint node
std::vector<AstConstraint*> m_clonedConstraints; // List of cloned global constraints
std::unordered_set<const AstVar*> m_processedVars; // Track by variable instance, not class
// METHODS
void markMembers(const AstClass* nodep) {
@ -196,18 +206,145 @@ class RandomizeMarkVisitor final : public VNVisitor {
staticRefp->classOrPackagep(VN_AS(staticRefp->varp()->user2p(), NodeModule));
}
}
void markNestedGlobalConstrainedRecurse(AstNode* nodep) {
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
AstVar* const varp = refp->varp();
if (varp->globalConstrained()) return;
varp->globalConstrained(true);
} else if (const AstMemberSel* const memberSelp = VN_CAST(nodep, MemberSel)) {
if (memberSelp->varp()) {
AstVar* const varp = memberSelp->varp();
if (varp->globalConstrained()) return;
varp->globalConstrained(true);
}
markNestedGlobalConstrainedRecurse(memberSelp->fromp());
}
}
// Build MemberSel chain from variable path
AstNodeExpr* buildMemberSelChain(AstVarRef* rootVarRefp, const std::vector<AstVar*>& path) {
AstNodeExpr* exprp = rootVarRefp->cloneTree(false);
for (AstVar* memberVarp : path) {
AstMemberSel* memberSelp
= new AstMemberSel{rootVarRefp->fileline(), exprp, memberVarp};
memberSelp->user2p(m_classp);
exprp = memberSelp;
}
return exprp;
}
// Process a single constraint during nested constraint cloning
void processNestedConstraint(AstConstraint* const constrp, AstVarRef* rootVarRefp,
const std::vector<AstVar*>& newPath) {
std::string pathPrefix = rootVarRefp->name();
for (AstVar* pathMemberVarp : newPath) {
pathPrefix += GLOBAL_CONSTRAINT_SEPARATOR + pathMemberVarp->name();
}
const std::string newName = pathPrefix + GLOBAL_CONSTRAINT_SEPARATOR + constrp->name();
for (const AstConstraint* existingConstrp : m_clonedConstraints) {
if (existingConstrp->name() == newName) {
// Multiple paths lead to same constraint - unsupported pattern
std::string fullPath = rootVarRefp->name();
for (AstVar* pathVar : newPath) { fullPath += "." + pathVar->name(); }
constrp->v3warn(E_UNSUPPORTED, "Unsupported: One variable '"
<< fullPath
<< "' cannot have multiple global constraints");
return;
}
}
AstConstraint* const cloneConstrp = constrp->cloneTree(false);
cloneConstrp->name(newName);
cloneConstrp->foreach([&](AstVarRef* varRefp) {
AstNodeExpr* const chainp = buildMemberSelChain(rootVarRefp, newPath);
AstMemberSel* const finalSelp
= new AstMemberSel{varRefp->fileline(), chainp, varRefp->varp()};
finalSelp->user2p(m_classp);
varRefp->replaceWith(finalSelp);
VL_DO_DANGLING(varRefp->deleteTree(), varRefp);
});
m_clonedConstraints.push_back(cloneConstrp);
}
// Clone constraints from nested rand class members
void cloneNestedConstraintsRecurse(AstVarRef* rootVarRefp, AstClass* classp,
const std::vector<AstVar*>& pathToClass) {
for (AstNode* memberNodep = classp->membersp(); memberNodep;
memberNodep = memberNodep->nextp()) {
AstVar* const memberVarp = VN_CAST(memberNodep, Var);
if (!memberVarp) continue;
if (!memberVarp->rand().isRandomizable()) continue;
const AstClassRefDType* const memberClassRefp
= VN_CAST(memberVarp->dtypep()->skipRefp(), ClassRefDType);
if (!memberClassRefp || !memberClassRefp->classp()) continue;
AstClass* nestedClassp = memberClassRefp->classp();
std::vector<AstVar*> newPath = pathToClass;
newPath.push_back(memberVarp);
// Replace all variable references inside the cloned constraint with proper
// member selections
nestedClassp->foreachMember(
[&](AstClass* const containingClassp, AstConstraint* const constrp) {
processNestedConstraint(constrp, rootVarRefp, newPath);
});
cloneNestedConstraintsRecurse(rootVarRefp, nestedClassp, newPath);
}
}
void cloneNestedConstraints(AstVarRef* rootVarRefp, AstClass* rootClass) {
std::vector<AstVar*> emptyPath;
cloneNestedConstraintsRecurse(rootVarRefp, rootClass, emptyPath);
}
void nameManipulation(AstVarRef* fromp, AstConstraint* cloneCons) {
cloneCons->name(fromp->name() + GLOBAL_CONSTRAINT_SEPARATOR + cloneCons->name());
cloneCons->foreach([&](AstVarRef* varRefp) {
AstVarRef* const clonedFromp = fromp->cloneTree(false);
AstMemberSel* const varMemberp
= new AstMemberSel{cloneCons->fileline(), clonedFromp, varRefp->varp()};
varMemberp->user2p(m_classp);
varRefp->replaceWith(varMemberp);
VL_DO_DANGLING(varRefp->deleteTree(), varRefp);
});
}
// Process a globally constrained variable by cloning its constraints
void processGlobalConstraint(AstVarRef* varRefp, AstClass* gConsClass) {
AstVar* const objVar = varRefp->varp();
// Process per-variable (object instance), not per-class
// This allows multiple objects of the same class (e.g., obj1 and obj2 of type Sub)
if (m_processedVars.insert(objVar).second) {
// Clone constraints from the top-level class (e.g., Level1 for obj_a)
gConsClass->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) {
AstConstraint* const cloneConstrp = constrp->cloneTree(false);
nameManipulation(varRefp, cloneConstrp);
m_clonedConstraints.push_back(cloneConstrp);
});
cloneNestedConstraints(varRefp, gConsClass);
}
}
// VISITORS
void visit(AstClass* nodep) override {
VL_RESTORER(m_classp);
VL_RESTORER(m_modp);
VL_RESTORER(m_clonedConstraints);
m_modp = m_classp = nodep;
iterateChildrenConst(nodep);
if (nodep->extendsp()) {
// Save pointer to derived class
// Record derived class for inheritance hierarchy tracking
const AstClass* const basep = nodep->extendsp()->classp();
m_baseToDerivedMap[basep].insert(nodep);
}
for (AstConstraint* const constrp : m_clonedConstraints) m_classp->addStmtsp(constrp);
m_clonedConstraints.clear();
}
void visit(AstNodeStmt* nodep) override {
VL_RESTORER(m_stmtp);
@ -464,7 +601,51 @@ class RandomizeMarkVisitor final : public VNVisitor {
// of type AstLambdaArgRef. They are randomized too.
const bool randObject = nodep->fromp()->user1() || VN_IS(nodep->fromp(), LambdaArgRef);
nodep->user1(randObject && nodep->varp()->rand().isRandomizable());
nodep->user2p(m_modp);
if (m_withp) {
AstNode* backp = m_withp;
while (backp->backp()) {
if (const AstMethodCall* const callp = VN_CAST(backp, MethodCall)) {
AstClassRefDType* classdtype
= VN_AS(callp->fromp()->dtypep()->skipRefp(), ClassRefDType);
nodep->user2p(classdtype->classp());
break;
}
backp = backp->backp();
}
} else {
nodep->user2p(m_modp);
}
if (randObject && nodep->varp()
&& nodep->varp()->rand().isRandomizable()) { // Process global constraints
if (m_classp && m_classp->user1() == IS_RANDOMIZED) {
m_classp->user1(IS_RANDOMIZED_GLOBAL);
}
// Mark the entire nested chain as participating in global constraints
if (VN_IS(nodep->fromp(), VarRef) || VN_IS(nodep->fromp(), MemberSel)) {
markNestedGlobalConstrainedRecurse(nodep->fromp());
} else if (VN_IS(nodep->fromp(), ArraySel)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: " << nodep->prettyTypeName()
<< " within a global constraint");
}
// Global constraint processing algorithm:
// 1. Detect globally constrained object variables in randomized classes
// 2. Clone constraint trees from the constrained object's class
// 3. Rename cloned constraints with object prefix (obj.var format)
// 4. Insert cloned constraints into current class for solver processing
// 5. Use basic randomization for non-constrained variables to avoid recursion
// Extract and validate components early to avoid repeated type checks
AstVarRef* const varRefp = VN_CAST(nodep->fromp(), VarRef);
if (!varRefp) return;
const AstClassRefDType* const classRefp
= VN_AS(varRefp->dtypep()->skipRefp(), ClassRefDType);
if (nodep->user1() && varRefp->varp()->globalConstrained()) {
processGlobalConstraint(varRefp, classRefp->classp());
}
}
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@ -479,6 +660,11 @@ class RandomizeMarkVisitor final : public VNVisitor {
nodep->user2p(m_modp);
iterateChildrenConst(nodep);
}
void visit(AstWith* nodep) override {
VL_RESTORER(m_withp);
m_withp = nodep;
iterateChildrenConst(nodep);
}
void visit(AstNodeExpr* nodep) override {
iterateChildrenConst(nodep);
@ -519,6 +705,20 @@ class ConstraintExprVisitor final : public VNVisitor {
bool m_structSel = false; // Marks when inside structSel
// (used to format "%@.%@" for struct arrays)
// Build full path for a MemberSel chain (e.g., "obj.l2.l3.l4")
std::string buildMemberPath(const AstMemberSel* const memberSelp) {
const AstNode* fromp = memberSelp->fromp();
if (const AstVarRef* const refp = VN_CAST(fromp, VarRef)) {
// Base case: reached root VarRef
return refp->name() + "." + memberSelp->name();
} else if (const AstMemberSel* const selp = VN_CAST(fromp, MemberSel)) {
// Recursive case: build path from outer levels
return buildMemberPath(selp) + "." + memberSelp->name();
}
memberSelp->v3fatalSrc("Unexpected node type in MemberSel chain");
return "";
}
AstSFormatF* getConstFormat(AstNodeExpr* nodep) {
return new AstSFormatF{nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false,
nodep};
@ -641,19 +841,30 @@ class ConstraintExprVisitor final : public VNVisitor {
CONSTRAINTIGN,
"Size constraint combined with element constraint may not work correctly");
}
AstMemberSel* membersel = VN_IS(nodep->backp(), MemberSel)
? VN_AS(nodep->backp(), MemberSel)->cloneTree(false)
: nullptr;
// Check if this variable is marked as globally constrained
const bool isGlobalConstrained = nodep->varp()->globalConstrained();
AstMemberSel* membersel = nullptr;
std::string smtName;
if (isGlobalConstrained && VN_IS(nodep->backp(), MemberSel)) {
// For global constraints: build complete path from topmost MemberSel
AstNode* topMemberSel = nodep->backp();
while (VN_IS(topMemberSel->backp(), MemberSel)) {
topMemberSel = topMemberSel->backp();
}
membersel = VN_AS(topMemberSel, MemberSel)->cloneTree(false);
smtName = buildMemberPath(membersel);
} else {
// No MemberSel: just variable name
smtName = nodep->name();
}
if (membersel) varp = membersel->varp();
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
const RandomizeMode randMode = {.asInt = varp->user1()};
if (!randMode.usesMode && editFormat(nodep)) return;
// In SMT just variable name, but we also ensure write_var for the variable
const std::string smtName = membersel
? membersel->fromp()->name() + "." + membersel->name()
: nodep->name(); // Can be anything unique
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr};
@ -666,12 +877,18 @@ class ConstraintExprVisitor final : public VNVisitor {
VCMethod::ARRAY_AT, new AstConst{nodep->fileline(), randMode.index}};
atp->dtypeSetUInt32();
exprp = new AstCond{varp->fileline(), atp, exprp, constFormatp};
} else {
} else if (!membersel || !isGlobalConstrained) {
// Only delete nodep here if it's not a global constraint
// Global constraints need nodep for write_var processing
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
relinker.relink(exprp);
if (!varp->user3()) {
// For global constraints: always call write_var with full path even if varp->user3() is
// set For normal constraints: only call write_var if varp->user3() is not set
if (!varp->user3() || (membersel && nodep->varp()->globalConstrained())) {
// For global constraints, delete nodep here after processing
if (membersel && isGlobalConstrained) VL_DO_DANGLING(pushDeletep(nodep), nodep);
AstCMethodHard* const methodp = new AstCMethodHard{
varp->fileline(),
new AstVarRef{varp->fileline(), VN_AS(m_genp->user2p(), NodeModule), m_genp,
@ -693,10 +910,14 @@ class ConstraintExprVisitor final : public VNVisitor {
methodp->dtypeSetVoid();
AstClass* const classp
= membersel ? VN_AS(membersel->user2p(), Class) : VN_AS(varp->user2p(), Class);
AstVarRef* const varRefp
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
varRefp->classOrPackagep(classOrPackagep);
membersel ? methodp->addPinsp(membersel) : methodp->addPinsp(varRefp);
if (membersel) {
methodp->addPinsp(membersel);
} else {
AstVarRef* const varRefp
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
varRefp->classOrPackagep(classOrPackagep);
methodp->addPinsp(varRefp);
}
AstNodeDType* tmpDtypep = varp->dtypep();
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
@ -911,8 +1132,21 @@ class ConstraintExprVisitor final : public VNVisitor {
editSMT(nodep, nodep->fromp(), indexp);
}
void visit(AstMemberSel* nodep) override {
if (nodep->user1()) {
nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)");
if (nodep->varp()->rand().isRandomizable() && nodep->fromp()) {
AstNode* rootNode = nodep->fromp();
while (const AstMemberSel* const selp = VN_CAST(rootNode, MemberSel))
rootNode = selp->fromp();
// Check if the root variable participates in global constraints
if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) {
AstVar* const constrainedVar = varRefp->varp();
if (constrainedVar->globalConstrained()) {
// Global constraint - unwrap the MemberSel
iterateChildren(nodep);
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
}
}
// Handle MemberSel references created by captureRefByThis()
if (VN_IS(nodep->fromp(), VarRef)
@ -1943,10 +2177,19 @@ class RandomizeVisitor final : public VNVisitor {
return;
}
AstFunc* const memberFuncp
= V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp());
= memberVarp->globalConstrained()
? V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp(),
BASIC_RANDOMIZE_FUNC_NAME)
: V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp());
AstMethodCall* const callp
= new AstMethodCall{fl, new AstVarRef{fl, classp, memberVarp, VAccess::WRITE},
"randomize", nullptr};
= memberVarp->globalConstrained()
? new AstMethodCall{fl,
new AstVarRef{fl, classp, memberVarp,
VAccess::WRITE},
BASIC_RANDOMIZE_FUNC_NAME, nullptr}
: new AstMethodCall{
fl, new AstVarRef{fl, classp, memberVarp, VAccess::WRITE},
"randomize", nullptr};
callp->taskp(memberFuncp);
callp->dtypeFrom(memberFuncp);
AstVarRef* const basicFvarRefReadp = basicFvarRefp->cloneTree(false);
@ -2123,6 +2366,8 @@ class RandomizeVisitor final : public VNVisitor {
if (!nodep->user1()) return; // Doesn't need randomize, or already processed
UINFO(9, "Define randomize() for " << nodep);
nodep->baseMostClassp()->needRNG(true);
const bool globalConstrained = nodep->user1() == IS_RANDOMIZED_GLOBAL;
AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep);
AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var);
addPrePostCall(nodep, randomizep, "pre_randomize");
@ -2184,7 +2429,18 @@ class RandomizeVisitor final : public VNVisitor {
}
AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE};
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp});
// For global constraints: call basic randomize first (without global constraints)
if (globalConstrained) {
AstFunc* const basicRandomizep
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME);
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, basicRandomizeCallp});
} else {
// For normal classes: use beginValp (standard flow)
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp});
}
if (AstTask* const resizeAllTaskp
= VN_AS(m_memberMap.findMember(nodep, "__Vresize_constrained_arrays"), Task)) {
@ -2192,15 +2448,23 @@ class RandomizeVisitor final : public VNVisitor {
randomizep->addStmtsp(resizeTaskRefp->makeStmt());
}
AstFunc* const basicRandomizep
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, "__Vbasic_randomize");
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
AstVarRef* const fvarRefReadp = fvarRefp->cloneTree(false);
fvarRefReadp->access(VAccess::READ);
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}});
// For global constraints: combine with solver result (beginValp)
// For normal classes: call basic randomize after resize
if (globalConstrained) {
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
new AstAnd{fl, fvarRefReadp, beginValp}});
} else {
AstFunc* const basicRandomizep
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME);
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
randomizep->addStmtsp(
new AstAssign{fl, fvarRefp->cloneTree(false),
new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}});
}
addPrePostCall(nodep, randomizep, "post_randomize");
nodep->user1(false);
}
@ -2417,7 +2681,7 @@ class RandomizeVisitor final : public VNVisitor {
randomizeFuncp->addStmtsp(localGenp);
AstFunc* const basicRandomizeFuncp
= V3Randomize::newRandomizeFunc(m_memberMap, classp, "__Vbasic_randomize");
= V3Randomize::newRandomizeFunc(m_memberMap, classp, BASIC_RANDOMIZE_FUNC_NAME);
AstFuncRef* const basicRandomizeFuncCallp
= new AstFuncRef{nodep->fileline(), basicRandomizeFuncp, nullptr};

View File

@ -58,6 +58,7 @@ class SliceVisitor final : public VNVisitor {
// STATE - across all visitors
VDouble0 m_statAssigns; // Statistic tracking
VDouble0 m_statSliceElementSkips; // Statistic tracking
// STATE - for current visit position (use VL_RESTORER)
AstNode* m_assignp = nullptr; // Assignment we are under
@ -248,6 +249,14 @@ class SliceVisitor final : public VNVisitor {
return false;
}
// Skip optimization if array is too large
const int elements = arrayp->rangep()->elementsConst();
const int elementLimit = v3Global.opt.fSliceElementLimit();
if (elements > elementLimit && elementLimit > 0) {
++m_statSliceElementSkips;
return false;
}
UINFO(4, "Slice optimizing " << nodep);
++m_statAssigns;
@ -256,7 +265,6 @@ class SliceVisitor final : public VNVisitor {
// Assign of an ascending range slice to a descending range one must reverse
// the elements
AstNodeAssign* newlistp = nullptr;
const int elements = arrayp->rangep()->elementsConst();
for (int elemIdx = 0; elemIdx < elements; ++elemIdx) {
// Original node is replaced, so it is safe to copy it one time even if it is impure.
AstNodeAssign* const newp
@ -383,6 +391,8 @@ public:
explicit SliceVisitor(AstNetlist* nodep) { iterate(nodep); }
~SliceVisitor() override {
V3Stats::addStat("Optimizations, Slice array assignments", m_statAssigns);
V3Stats::addStat("Optimizations, Slice array skips due to size limit",
m_statSliceElementSkips);
}
};

View File

@ -50,8 +50,8 @@ class StatsReport final {
V3Statistic* lastp = nullptr;
for (const auto& itr : byName) {
V3Statistic* repp = itr.second;
if (lastp && lastp->sumit() && lastp->printit() && lastp->name() == repp->name()
&& lastp->stage() == repp->stage()) {
if (lastp && lastp->sumit() && lastp->printit() && repp->printit()
&& lastp->name() == repp->name() && lastp->stage() == repp->stage()) {
lastp->combineWith(repp);
} else {
lastp = repp;

View File

@ -215,6 +215,9 @@ class WidthVisitor final : public VNVisitor {
V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
WidthVP* m_vup = nullptr; // Current node state
bool m_underFork = false; // Visiting under a fork
bool m_underSExpr = false; // Visiting under a sequence expression
AstNode* m_seqUnsupp = nullptr; // Property has unsupported node
bool m_hasSExpr = false; // Property has a sequence expression
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
@ -1539,6 +1542,7 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstImplication* nodep) override {
m_seqUnsupp = nodep;
if (m_vup->prelim()) {
iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
@ -1557,9 +1561,15 @@ class WidthVisitor final : public VNVisitor {
}
}
void visit(AstSExpr* nodep) override {
VL_RESTORER(m_underSExpr);
m_underSExpr = true;
m_hasSExpr = true;
if (m_vup->prelim()) {
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
if (nodep->preExprp()) {
iterateCheckBool(nodep, "preExprp", nodep->preExprp(), BOTH);
}
iterate(nodep->delayp());
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
nodep->dtypeSetBit();
}
}
@ -5338,6 +5348,8 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstPropSpec* nodep) override {
VL_RESTORER(m_seqUnsupp);
VL_RESTORER(m_hasSExpr);
if (m_vup->prelim()) { // First stage evaluation
iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);
userIterateAndNext(nodep->sensesp(), nullptr);
@ -5346,6 +5358,20 @@ class WidthVisitor final : public VNVisitor {
BOTH); // it's like an if() condition.
}
nodep->dtypeSetBit();
if (m_hasSExpr) {
if (VN_IS(m_seqUnsupp, Implication)) {
m_seqUnsupp->v3warn(E_UNSUPPORTED,
"Unsupported: Implication with sequence expression");
AstConst* const newp = new AstConst{nodep->fileline(), 0};
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (nodep->disablep()) {
nodep->disablep()->v3warn(E_UNSUPPORTED,
"Unsupported: Disable iff with sequence expression");
VL_DO_DANGLING(pushDeletep(nodep->disablep()->unlinkFrBack()), nodep);
}
}
}
}
@ -7008,6 +7034,13 @@ class WidthVisitor final : public VNVisitor {
if (m_vup->prelim()) {
iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH);
nodep->dtypeSetBit();
if (m_underSExpr) {
nodep->v3error("Unexpected 'not' in sequence expression context");
AstConst* const newp = new AstConst{nodep->fileline(), 0};
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
}
void visit_log_and_or(AstNodeBiop* nodep) {

View File

@ -20,6 +20,7 @@
#include "V3ActiveTop.h"
#include "V3Assert.h"
#include "V3AssertPre.h"
#include "V3AssertProp.h"
#include "V3Ast.h"
#include "V3Begin.h"
#include "V3Branch.h"
@ -251,6 +252,8 @@ static void process() {
// Assertion insertion
// After we've added block coverage, but before other nasty transforms
V3AssertProp::assertPropAll(v3Global.rootp());
//
V3AssertPre::assertPreAll(v3Global.rootp());
//
V3Assert::assertAll(v3Global.rootp());

View File

@ -6538,7 +6538,8 @@ pexpr<nodeExprp>: // IEEE: property_expr (The name pexpr is important as regex
// // IEEE: '(' pexpr ')'
// // Expanded below
//
yNOT pexpr %prec prNEGATION { $$ = new AstLogNot{$1, $2}; }
yNOT pexpr
{ $$ = new AstLogNot{$1, $2}; }
| ySTRONG '(' sexpr ')'
{ $$ = $3; BBUNSUP($2, "Unsupported: strong (in property expression)"); }
| yWEAK '(' sexpr ')'
@ -6628,7 +6629,7 @@ sexpr<nodeExprp>: // ==IEEE: sequence_expr (The name sexpr is important as reg
cycle_delay_range ~p~sexpr %prec yP_POUNDPOUND
{ $$ = new AstSExpr{$<fl>1, $1, $2}; }
| ~p~sexpr cycle_delay_range sexpr %prec prPOUNDPOUND_MULTI
{ $$ = $1; BBUNSUP($2->fileline(), "Unsupported: ## (in sequence expression)"); DEL($2, $3); }
{ $$ = new AstSExpr{$<fl>2, $1, $2, $3}; }
//
// // IEEE: expression_or_dist [ boolean_abbrev ]
// // Note expression_or_dist includes "expr"!

View File

@ -1476,12 +1476,14 @@ class VlTest:
tee=param['tee'])
elif param['iv']:
cmd = [
run_env + self.obj_dir + "/simiv", ' '.join(param['iv_run_flags']),
' '.join(param['all_run_flags'])
run_env + 'vvp', ' '.join(param['iv_run_flags']), ' '.join(param['all_run_flags'])
]
if param['use_libvpi']:
# Don't enter command line on $stop, include vpi
cmd += ["vvp -n -m " + self.obj_dir + "/libvpi.so"]
# Don't enter command line on $stop
cmd += ["-n"]
# include vpi
cmd += ["-m", self.obj_dir + "/libvpi.so"]
cmd += [self.obj_dir + "/simiv"]
self.run(cmd=cmd,
check_finished=param['check_finished'],
entering=param['entering'],

View File

@ -0,0 +1,26 @@
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:41:20: Unsupported: MEMBERSEL 'm_x' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
41 | m_mid.m_arr[0].m_x == 200;
| ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:42:20: Unsupported: MEMBERSEL 'm_y' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
42 | m_mid.m_arr[0].m_y == 201;
| ^~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:18: Unsupported: MEMBERSEL 'm_obj' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
47 | m_mid_arr[0].m_obj.m_x == 300;
| ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:18: Unsupported: MEMBERSEL 'm_obj' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
48 | m_mid_arr[0].m_obj.m_y == 301;
| ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:18: Unsupported: MEMBERSEL 'm_arr' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
53 | m_mid_arr[1].m_arr[2].m_y == 400;
| ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:27: Unsupported: MEMBERSEL 'm_y' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
53 | m_mid_arr[1].m_arr[2].m_y == 400;
| ^~~
%Error: Exiting due to

View File

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

View File

@ -0,0 +1,86 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
/* verilator lint_off WIDTHTRUNC */
class Inner;
rand int m_x;
rand int m_y;
endclass
class Middle;
rand Inner m_obj;
rand Inner m_arr[3];
endclass
class Outer;
rand Middle m_mid;
rand Middle m_mid_arr[2];
function new();
m_mid = new;
m_mid.m_obj = new;
foreach (m_mid.m_arr[i]) m_mid.m_arr[i] = new;
foreach (m_mid_arr[i]) begin
m_mid_arr[i] = new;
m_mid_arr[i].m_obj = new;
foreach (m_mid_arr[i].m_arr[j]) m_mid_arr[i].m_arr[j] = new;
end
endfunction
// Case 1: Simple nested member access (should work)
constraint c_simple {
m_mid.m_obj.m_x == 100;
m_mid.m_obj.m_y == 101;
}
// Case 2: Array indexing in the path (may not work)
constraint c_array_index {
m_mid.m_arr[0].m_x == 200;
m_mid.m_arr[0].m_y == 201;
}
// Case 3: Nested array indexing
constraint c_nested_array {
m_mid_arr[0].m_obj.m_x == 300;
m_mid_arr[0].m_obj.m_y == 301;
}
// Case 4: Multiple array indices
constraint c_multi_array {
m_mid_arr[1].m_arr[2].m_y == 400;
}
endclass
module t_constraint_global_arr_unsup;
initial begin
Outer o = new;
if (o.randomize()) begin
$display("Case 1 - Simple: mid.obj.x = %0d (expected 100)", o.m_mid.m_obj.m_x);
$display("Case 1 - Simple: mid.obj.y = %0d (expected 101)", o.m_mid.m_obj.m_y);
$display("Case 2 - Array[0]: mid.arr[0].x = %0d (expected 200)", o.m_mid.m_arr[0].m_x);
$display("Case 2 - Array[0]: mid.arr[0].y = %0d (expected 201)", o.m_mid.m_arr[0].m_y);
$display("Case 3 - Nested[0]: mid_arr[0].obj.x = %0d (expected 300)", o.m_mid_arr[0].m_obj.m_x);
$display("Case 3 - Nested[0]: mid_arr[0].obj.y = %0d (expected 301)", o.m_mid_arr[0].m_obj.m_y);
$display("Case 4 - Multi[1][2]: mid_arr[1].arr[2].y = %0d (expected 400)", o.m_mid_arr[1].m_arr[2].m_y);
// Check results
if (o.m_mid.m_obj.m_x == 100 && o.m_mid.m_obj.m_y == 101 &&
o.m_mid.m_arr[0].m_x == 200 && o.m_mid.m_arr[0].m_y == 201 &&
o.m_mid_arr[0].m_obj.m_x == 300 && o.m_mid_arr[0].m_obj.m_y == 301 &&
o.m_mid_arr[1].m_arr[2].m_y == 400) begin
$display("*-* All Finished *-*");
$finish;
end else begin
$display("*-* FAILED *-*");
$stop;
end
end else begin
$display("*-* FAILED: randomize() returned 0 *-*");
$stop;
end
end
endmodule
/* verilator lint_off WIDTHTRUNC */

View File

@ -0,0 +1,6 @@
%Error-UNSUPPORTED: t/t_constraint_global_nested_unsup.v:9:14: Unsupported: One variable 'm_mid.m_inner' cannot have multiple global constraints
: ... note: In instance 't'
9 | constraint c_inner { m_val inside {[1:10]}; }
| ^~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

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

View File

@ -0,0 +1,51 @@
// DESCRIPTION: Verilator: Test for unsupported multiple global constraints
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class Inner;
rand int m_val;
constraint c_inner { m_val inside {[1:10]}; }
function new(); m_val = 0; endfunction
endclass
class Mid;
rand Inner m_inner;
rand int m_x;
// Mid has global constraint on m_inner.m_val
constraint c_mid_global {
m_x > m_inner.m_val;
m_x inside {[5:15]};
}
function new();
m_inner = new();
m_x = 0;
endfunction
endclass
class Top;
rand Mid m_mid;
rand int m_y;
// Top also has global constraint on m_mid.m_inner.m_val
constraint c_top_global {
m_y < m_mid.m_inner.m_val;
m_y inside {[1:5]};
}
function new();
m_mid = new();
m_y = 0;
endfunction
endclass
module t;
Top top;
/* verilator lint_off WIDTHTRUNC */
initial begin
top = new();
if (!top.randomize()) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
/* verilator lint_off WIDTHTRUNC */
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
if not test.have_solver:
test.skip("No constraint solver installed")
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,124 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class Inner;
rand int m_val;
constraint c_local { m_val inside {[1:5]}; }
class NestedInner;
rand int nested_val;
constraint c_nested { nested_val inside {[1:3]}; }
endclass
rand NestedInner nested_obj;
function new();
m_val = 0;
nested_obj = new();
endfunction
endclass
class Mid;
int m_limit;
rand int m_x;
rand Inner m_inner;
constraint c_mid { m_x == m_limit; }
function new(int lim);
m_limit = lim;
m_inner = new();
endfunction
endclass
class Top;
rand Mid m_m1;
rand Mid m_m2;
rand int m_y;
constraint c_global {
m_m1.m_inner.m_val < m_m2.m_inner.m_val;
m_y > m_m1.m_x;
m_y < m_m2.m_x;
m_m1.m_inner.m_val + m_m2.m_inner.m_val < 8;
// Global constraint on nested class variable (3-level deep)
m_m1.m_inner.nested_obj.nested_val == 1;
m_m2.m_inner.nested_obj.nested_val == 3;
}
function new();
m_m1 = new(3);
m_m2 = new(5);
m_y = 0;
endfunction
endclass
// Second independent class with global constraints
class AnotherTop;
rand Mid m_m3;
rand int m_z;
constraint c_another {
m_z < m_m3.m_x;
}
function new();
m_m3 = new(10);
m_z = 0;
endfunction
endclass
module t_constraint_global_random;
int success;
Top t;
AnotherTop t2;
initial begin
t = new();
// Test 1: Regular randomize() with global constraints
success = t.randomize();
if (success != 1) $stop;
if (t.m_m1.m_x != 3 || t.m_m2.m_x != 5) $stop;
if (t.m_m1.m_inner.m_val >= t.m_m2.m_inner.m_val) $stop;
if (t.m_y <= t.m_m1.m_x || t.m_y >= t.m_m2.m_x) $stop;
if (t.m_m1.m_inner.m_val + t.m_m2.m_inner.m_val >= 8) $stop;
if (t.m_m1.m_inner.m_val < 1 || t.m_m1.m_inner.m_val > 5 ||
t.m_m2.m_inner.m_val < 1 || t.m_m2.m_inner.m_val > 5) $stop;
// Verify nested class global constraints (3-level deep: Top -> Mid -> Inner -> NestedInner)
if (t.m_m1.m_inner.nested_obj.nested_val != 1) $stop;
if (t.m_m2.m_inner.nested_obj.nested_val != 3) $stop;
// Test 2: randomize() with inline constraint on global-constrained members
success = 0;
success = t.randomize() with {
m_m1.m_inner.m_val == 2;
m_m2.m_inner.m_val == 5;
};
if (success != 1) $stop;
// Verify inline constraints
if (t.m_m1.m_inner.m_val != 2) $stop;
if (t.m_m2.m_inner.m_val != 5) $stop;
// Verify global constraints still hold
if (t.m_m1.m_x != 3 || t.m_m2.m_x != 5) $stop;
if (t.m_m1.m_inner.m_val >= t.m_m2.m_inner.m_val) $stop;
if (t.m_y <= t.m_m1.m_x || t.m_y >= t.m_m2.m_x) $stop;
if (t.m_m1.m_inner.m_val + t.m_m2.m_inner.m_val >= 8) $stop;
// Test 3: Second independent class (tests m_clonedConstraints.clear() bug)
t2 = new();
success = t2.randomize();
if (success != 1) $stop;
if (t2.m_z >= t2.m_m3.m_x) $stop;
if (t2.m_m3.m_x != 10) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,339 @@
// // verilator_coverage annotation
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc, bump, result;
logic foo;
%000001 initial begin
-000001 point: comment=block hier=top.t
%000001 cyc = 0;
-000001 point: comment=block hier=top.t
%000001 foo = '1;
-000001 point: comment=block hier=top.t
end
000010 always @(posedge clk) begin
+000010 point: comment=block hier=top.t
~000010 if (($time != 0) && foo) bump <= bump + 1;
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($realtime != 0) && foo) bump <= bump + 1;
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($stime != 0) && foo) bump <= bump + 1;
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($bitstoreal(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($bitstoreal(64'h7b) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($bitstoreal(64'h7b) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($itor(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($itor($signed(32'sh7b)) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($itor($signed(32'sh7b)) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($signed(3) != 0) && foo) bump <= bump + 1;
-000000 point: comment=((32'sh3 != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=((32'sh3 != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($realtobits(1.23) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($realtobits(1.23) != 64'h0)==0) => 0 hier=top.t
+000010 point: comment=(($realtobits(1.23) != 64'h0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($rtoi(1.23) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($rtoi(1.23) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($rtoi(1.23) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($unsigned(-3) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(((- 32'sh3) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(((- 32'sh3) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($clog2(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($clog2(32'sh7b) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($clog2(32'sh7b) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($ln(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($ln($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($ln($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($log10(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($log10($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($log10($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($exp(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($exp($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($exp($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($sqrt(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($sqrt($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($sqrt($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($pow(123, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=((($itor($signed(32'sh7b)) ** $itor($signed(32'sh2))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=((($itor($signed(32'sh7b)) ** $itor($signed(32'sh2))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($floor(1.23) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($floor(1.23) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($floor(1.23) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($ceil(1.23) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($ceil(1.23) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($ceil(1.23) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($sin(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($sin($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($sin($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($cos(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($cos($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($cos($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($tan(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($tan($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($tan($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($asin(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($asin($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($asin($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($acos(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($acos($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($acos($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($atan(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($atan($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($atan($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($atan2(123, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($atan2($itor($signed(32'sh7b)),$itor($signed(32'sh2))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($atan2($itor($signed(32'sh7b)),$itor($signed(32'sh2))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($hypot(123, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($hypot($itor($signed(32'sh7b)),$itor($signed(32'sh2))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($hypot($itor($signed(32'sh7b)),$itor($signed(32'sh2))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($sinh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($sinh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($sinh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($cosh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($cosh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($cosh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($tanh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($tanh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($tanh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($asinh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($asinh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($asinh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($acosh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($acosh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($acosh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($atanh(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($atanh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==0) => 0 hier=top.t
+000010 point: comment=(($atanh($itor($signed(32'sh7b))) != $itor($signed(32'sh0)))==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($countbits(123, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($countbits(32'sh7b, 32'sh2, , 32'sh2) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($countbits(32'sh7b, 32'sh2, , 32'sh2) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($onehot(123) != 0) && foo) bump <= bump + 1;
+000010 point: comment=(($onehot(32'sh7b) != 32'sh0)==0) => 0 hier=top.t
-000000 point: comment=(($onehot(32'sh7b) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
~000010 if ($isunknown(foo) && foo) bump <= bump + 1;
+000010 point: comment=($isunknown(foo)==0) => 0 hier=top.t
-000000 point: comment=($isunknown(foo)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
~000010 if (($countones(123) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($countones(32'sh7b) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($countones(32'sh7b) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($onehot0(123) != 0) && foo) bump <= bump + 1;
+000010 point: comment=(($onehot0(32'sh7b) != 32'sh0)==0) => 0 hier=top.t
-000000 point: comment=(($onehot0(32'sh7b) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
~000010 if (($sampled(foo) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($sampled(foo) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($sampled(foo) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($fell(foo) != 0) && foo) bump <= bump + 1;
+000010 point: comment=(($fell(foo) != 32'sh0)==0) => 0 hier=top.t
-000000 point: comment=(($fell(foo) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
%000009 if (($changed(foo) != 0) && foo) bump <= bump + 1;
-000009 point: comment=(((! $stable(foo)) != 32'sh0)==0) => 0 hier=top.t
-000001 point: comment=(((! $stable(foo)) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000001 point: comment=if hier=top.t
-000009 point: comment=else hier=top.t
%000009 if (($rose(foo) != 0) && foo) bump <= bump + 1;
-000009 point: comment=(($rose(foo) != 32'sh0)==0) => 0 hier=top.t
-000001 point: comment=(($rose(foo) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000001 point: comment=if hier=top.t
-000009 point: comment=else hier=top.t
%000009 if (($stable(foo) != 0) && foo) bump <= bump + 1;
-000001 point: comment=(($stable(foo) != 32'sh0)==0) => 0 hier=top.t
-000009 point: comment=(($stable(foo) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000009 point: comment=if hier=top.t
-000001 point: comment=else hier=top.t
%000009 if (($past(foo) != 0) && foo) bump <= bump + 1;
-000001 point: comment=(($past(foo) != 32'sh0)==0) => 0 hier=top.t
-000009 point: comment=(($past(foo) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000009 point: comment=if hier=top.t
-000001 point: comment=else hier=top.t
~000010 if (($random != 0) && foo) bump <= bump + 1;
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_erlang(result, 2, 3) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_erlang(result, 32'sh2, 32'sh3) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($dist_erlang(result, 32'sh2, 32'sh3) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_normal(result, 2, 3) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_normal(result, 32'sh2, 32'sh3) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($dist_normal(result, 32'sh2, 32'sh3) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_t(result, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_t(result, 32'sh2) != 32'sh0)==0) => 0 hier=top.t
-000000 point: comment=(($dist_t(result, 32'sh2) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
~000010 if (($dist_chi_square(result, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_chi_square(result, 32'sh2) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($dist_chi_square(result, 32'sh2) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_exponential(result, 2) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_exponential(result, 32'sh2) != 32'sh0)==0) => 0 hier=top.t
-000000 point: comment=(($dist_exponential(result, 32'sh2) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_poisson(result, 2) != 0) && foo) bump <= bump + 1;
+000010 point: comment=(($dist_poisson(result, 32'sh2) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($dist_poisson(result, 32'sh2) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($dist_uniform(result, 2, 3) != 0) && foo) bump <= bump + 1;
-000000 point: comment=(($dist_uniform(result, 32'sh2, 32'sh3) != 32'sh0)==0) => 0 hier=top.t
+000010 point: comment=(($dist_uniform(result, 32'sh2, 32'sh3) != 32'sh0)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
~000010 if (($sformatf("abc") != "abc") && foo) bump <= bump + 1;
+000010 point: comment=(($sformatf(%22abc%22);%0A != %22abc%22)==0) => 0 hier=top.t
-000000 point: comment=(($sformatf(%22abc%22);%0A != %22abc%22)==1 && foo==1) => 1 hier=top.t
-000000 point: comment=(foo==0) => 0 hier=top.t
-000000 point: comment=if hier=top.t
+000010 point: comment=else hier=top.t
~000010 if (foo && foo) bump <= bump + 1;
-000000 point: comment=(foo==0) => 0 hier=top.t
+000010 point: comment=(foo==1) => 1 hier=top.t
+000010 point: comment=if hier=top.t
-000000 point: comment=else hier=top.t
000010 cyc <= cyc + 1;
+000010 point: comment=block hier=top.t
%000009 if (cyc==9) begin
-000001 point: comment=if hier=top.t
-000009 point: comment=else hier=top.t
%000001 $write("*-* All Finished *-*\n");
-000001 point: comment=if hier=top.t
%000001 $finish;
-000001 point: comment=if hier=top.t
end
end
endmodule

View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("simulator")
test.compile(verilator_flags2=["--cc --coverage-line --coverage-expr"])
test.execute()
test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
"--annotate-points",
"--annotate", test.obj_dir + "/annotated",
test.obj_dir + "/coverage.dat"],
verilator_run=True) # yapf:disable
test.files_identical(test.obj_dir + "/annotated/t_cover_sys_line_expr.v", test.golden_filename)
test.passes()

View File

@ -0,0 +1,81 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc, bump, result;
logic foo;
initial begin
cyc = 0;
foo = '1;
end
always @(posedge clk) begin
if (($time != 0) && foo) bump <= bump + 1;
if (($realtime != 0) && foo) bump <= bump + 1;
if (($stime != 0) && foo) bump <= bump + 1;
if (($bitstoreal(123) != 0) && foo) bump <= bump + 1;
if (($itor(123) != 0) && foo) bump <= bump + 1;
if (($signed(3) != 0) && foo) bump <= bump + 1;
if (($realtobits(1.23) != 0) && foo) bump <= bump + 1;
if (($rtoi(1.23) != 0) && foo) bump <= bump + 1;
if (($unsigned(-3) != 0) && foo) bump <= bump + 1;
if (($clog2(123) != 0) && foo) bump <= bump + 1;
if (($ln(123) != 0) && foo) bump <= bump + 1;
if (($log10(123) != 0) && foo) bump <= bump + 1;
if (($exp(123) != 0) && foo) bump <= bump + 1;
if (($sqrt(123) != 0) && foo) bump <= bump + 1;
if (($pow(123, 2) != 0) && foo) bump <= bump + 1;
if (($floor(1.23) != 0) && foo) bump <= bump + 1;
if (($ceil(1.23) != 0) && foo) bump <= bump + 1;
if (($sin(123) != 0) && foo) bump <= bump + 1;
if (($cos(123) != 0) && foo) bump <= bump + 1;
if (($tan(123) != 0) && foo) bump <= bump + 1;
if (($asin(123) != 0) && foo) bump <= bump + 1;
if (($acos(123) != 0) && foo) bump <= bump + 1;
if (($atan(123) != 0) && foo) bump <= bump + 1;
if (($atan2(123, 2) != 0) && foo) bump <= bump + 1;
if (($hypot(123, 2) != 0) && foo) bump <= bump + 1;
if (($sinh(123) != 0) && foo) bump <= bump + 1;
if (($cosh(123) != 0) && foo) bump <= bump + 1;
if (($tanh(123) != 0) && foo) bump <= bump + 1;
if (($asinh(123) != 0) && foo) bump <= bump + 1;
if (($acosh(123) != 0) && foo) bump <= bump + 1;
if (($atanh(123) != 0) && foo) bump <= bump + 1;
if (($countbits(123, 2) != 0) && foo) bump <= bump + 1;
if (($onehot(123) != 0) && foo) bump <= bump + 1;
if ($isunknown(foo) && foo) bump <= bump + 1;
if (($countones(123) != 0) && foo) bump <= bump + 1;
if (($onehot0(123) != 0) && foo) bump <= bump + 1;
if (($sampled(foo) != 0) && foo) bump <= bump + 1;
if (($fell(foo) != 0) && foo) bump <= bump + 1;
if (($changed(foo) != 0) && foo) bump <= bump + 1;
if (($rose(foo) != 0) && foo) bump <= bump + 1;
if (($stable(foo) != 0) && foo) bump <= bump + 1;
if (($past(foo) != 0) && foo) bump <= bump + 1;
if (($random != 0) && foo) bump <= bump + 1;
if (($dist_erlang(result, 2, 3) != 0) && foo) bump <= bump + 1;
if (($dist_normal(result, 2, 3) != 0) && foo) bump <= bump + 1;
if (($dist_t(result, 2) != 0) && foo) bump <= bump + 1;
if (($dist_chi_square(result, 2) != 0) && foo) bump <= bump + 1;
if (($dist_exponential(result, 2) != 0) && foo) bump <= bump + 1;
if (($dist_poisson(result, 2) != 0) && foo) bump <= bump + 1;
if (($dist_uniform(result, 2, 3) != 0) && foo) bump <= bump + 1;
if (($sformatf("abc") != "abc") && foo) bump <= bump + 1;
if (foo && foo) bump <= bump + 1;
cyc <= cyc + 1;
if (cyc==9) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -277,6 +277,7 @@ module Vt_debug_emitv_t;
str = $sformatf("cyc=%~", cyc);
;
$display("str = %@", str);
$display("struct = %@", ps);
$display("%% [%t] [%^] to=%o td=%d", $time,
$realtime, $time, $time);
$sscanf(40'h666f6f3d35, "foo=%d", i);
@ -365,9 +366,6 @@ module Vt_debug_emitv_t;
property p1;
@( clk) sum[0]
endproperty
property p2;
@(posedge clk) disable iff (cyc == 'sh1) ##1 sum[0]
endproperty
assert property (@( clk) (! ##1 in)
) begin
end

View File

@ -218,6 +218,7 @@ module t (/*AUTOARG*/
str = $sformatf("cyc=%d", cyc);
$display("str = %s", str);
$display("struct = %p", ps);
$display("%% [%t] [%t] to=%o td=%d", $time, $realtime, $time, $time);
$sscanf("foo=5", "foo=%d", i);
$printtimescale;
@ -276,9 +277,6 @@ module t (/*AUTOARG*/
property p1;
@(clk) sum[0]
endproperty
property p2;
@(posedge clk) disable iff (cyc == 1) ##1 sum[0]
endproperty
assert property (@(clk) not ##1 in);

View File

@ -1,19 +1,10 @@
%Error-UNSUPPORTED: t/t_expect.v:19:32: Unsupported: ## (in sequence expression)
19 | expect (@(posedge clk) a ##1 b) a = 110;
| ^~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_expect.v:19:7: Unsupported: expect
19 | expect (@(posedge clk) a ##1 b) a = 110;
| ^~~~~~
%Error-UNSUPPORTED: t/t_expect.v:21:32: Unsupported: ## (in sequence expression)
21 | expect (@(posedge clk) a ##1 b) else a = 299;
| ^~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_expect.v:21:7: Unsupported: expect
21 | expect (@(posedge clk) a ##1 b) else a = 299;
| ^~~~~~
%Error-UNSUPPORTED: t/t_expect.v:23:32: Unsupported: ## (in sequence expression)
23 | expect (@(posedge clk) a ##1 b) a = 300; else a = 399;
| ^~
%Error-UNSUPPORTED: t/t_expect.v:23:7: Unsupported: expect
23 | expect (@(posedge clk) a ##1 b) a = 300; else a = 399;
| ^~~~~~

View File

@ -39,6 +39,7 @@ module t (/*AUTOARG*/
$write("[%0t] cyc==%0d sum=%x\n", $time, cyc, w[CNT]);
`endif
if (w[CNT] !== `EXPECTED_SUM) $stop;
$display("cyc: %0d $past(cyc): %0d", cyc, $past(cyc));
$write("*-* All Finished *-*\n");
$finish;
end
@ -55,7 +56,7 @@ module sub (input clk, input [31:0] i, output [31:0] z);
assign z = z_tmp;
always @(posedge z_tmp == 32'b11) begin
$display("%m z_tmp[0]: %0d", z_tmp);
$display("%m z_tmp: %0d, $past(z_tmp): $0d", z_tmp, $past(z_tmp));
end
endmodule

View File

@ -125,7 +125,7 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2")
# Check combine count
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (219 if test.vltmt else 205))
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (239 if test.vltmt else 222))
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2)
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2)

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats', '--fslice-element-limit', '10'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 4)
test.passes()

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (
input logic [7:0] i1 [8],
input logic [7:0] i2 [16],
input logic [7:0] i3 [512],
output logic [7:0] o1 [8],
output logic [7:0] o2 [16],
output logic [7:0] o3 [256]
);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats', '--fslice-element-limit', '0'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 0)
test.passes()

View File

@ -0,0 +1,2 @@
%Error: --fslice-element-limit must be >= 0: -100
... See the manual at https://verilator.org/verilator_doc.html?v=5.043 for more assistance.

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.top_filename = "t/t_opt_slice_element_limit.v"
test.golden_filename = "t/t_opt_slice_element_limit_bad.out"
test.lint(fails=True,
verilator_flags2=['--stats', '--fslice-element-limit', '-100'],
except_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 1)
test.passes()

View File

@ -1,140 +1,134 @@
[4] single delay with const stmt, fileline:115
[5] concurrent assert else, fileline:167
[6] single delay with const stmt, fileline:115
[7] concurrent assert stmt, fileline:166
[8] single delay with const stmt, fileline:115
[9] concurrent assert else, fileline:167
[11] concurrent assert else, fileline:167
[12] single delay with var stmt, fileline:118
[13] concurrent assert else, fileline:167
[14] single delay with var else, fileline:119
[15] concurrent assert stmt, fileline:166
[16] single delay with var stmt, fileline:118
[17] concurrent assert else, fileline:167
[18] single delay with var else, fileline:119
[19] concurrent assert else, fileline:167
[20] single delay with var stmt, fileline:118
[21] concurrent assert else, fileline:167
[23] concurrent assert stmt, fileline:166
[25] concurrent assert else, fileline:167
[26] single multi-cycle delay with var else, fileline:123
[27] concurrent assert else, fileline:167
[28] single multi-cycle delay with var stmt, fileline:122
[29] concurrent assert else, fileline:167
[30] single multi-cycle delay with var else, fileline:123
[31] concurrent assert stmt, fileline:166
[33] concurrent assert else, fileline:167
[34] single delay with var brackets 1 else, fileline:127
[35] concurrent assert else, fileline:167
[36] single delay with var brackets 1 stmt, fileline:126
[37] concurrent assert else, fileline:167
[38] single delay with var brackets 1 else, fileline:127
[39] concurrent assert stmt, fileline:166
[40] single delay with var brackets 1 stmt, fileline:126
[41] concurrent assert else, fileline:167
[43] concurrent assert else, fileline:167
[44] single delay with var brackets 2 stmt, fileline:130
[45] concurrent assert else, fileline:167
[46] single delay with var brackets 2 else, fileline:131
[47] concurrent assert stmt, fileline:166
[48] single delay with var brackets 2 stmt, fileline:130
[49] concurrent assert else, fileline:167
[50] single delay with var brackets 2 else, fileline:131
[51] concurrent assert else, fileline:167
[53] concurrent assert else, fileline:167
[54] single delay with negated var else, fileline:135
[55] concurrent assert stmt, fileline:166
[56] single delay with negated var stmt, fileline:134
[57] concurrent assert else, fileline:167
[58] single delay with negated var else, fileline:135
[59] concurrent assert else, fileline:167
[60] single delay with negated var else, fileline:135
[61] concurrent assert else, fileline:167
[63] concurrent assert stmt, fileline:166
[64] single delay with negated var else, fileline:139
[65] concurrent assert else, fileline:167
[66] single delay with negated var stmt, fileline:138
[67] concurrent assert else, fileline:167
[68] single delay with negated var else, fileline:139
[69] concurrent assert else, fileline:167
[70] single delay with negated var stmt, fileline:138
[71] concurrent assert stmt, fileline:166
[73] concurrent assert else, fileline:167
[74] single delay with negated var brackets stmt, fileline:142
[75] concurrent assert else, fileline:167
[76] single delay with negated var brackets else, fileline:144
[77] concurrent assert else, fileline:167
[78] single delay with negated var brackets stmt, fileline:142
[79] concurrent assert stmt, fileline:166
[80] single delay with negated var brackets else, fileline:144
[81] concurrent assert else, fileline:167
[83] concurrent assert else, fileline:167
[84] single delay with negated var brackets else, fileline:148
[85] concurrent assert else, fileline:167
[87] concurrent assert stmt, fileline:166
[88] single delay with negated var brackets else, fileline:148
[89] concurrent assert else, fileline:167
[91] concurrent assert else, fileline:167
[93] concurrent assert else, fileline:167
[94] single delay with nested not else, fileline:152
[95] concurrent assert stmt, fileline:166
[96] single delay with nested not stmt, fileline:151
[97] concurrent assert else, fileline:167
[98] single delay with nested not else, fileline:152
[99] concurrent assert else, fileline:167
[100] single delay with nested not stmt, fileline:151
[101] concurrent assert else, fileline:167
[103] concurrent assert stmt, fileline:166
[105] concurrent assert else, fileline:167
[107] concurrent assert else, fileline:167
[109] concurrent assert else, fileline:167
[111] concurrent assert stmt, fileline:166
[113] concurrent assert else, fileline:167
[114] property, fileline:162
[115] concurrent assert else, fileline:167
[116] property, fileline:163
[117] concurrent assert else, fileline:167
[118] property, fileline:162
[119] concurrent assert stmt, fileline:166
[120] property, fileline:163
[121] concurrent assert else, fileline:167
[123] concurrent assert else, fileline:167
[125] concurrent assert else, fileline:167
[127] concurrent assert stmt, fileline:166
[129] concurrent assert else, fileline:167
[131] concurrent assert else, fileline:167
[133] concurrent assert else, fileline:167
[135] concurrent assert stmt, fileline:166
[137] concurrent assert else, fileline:167
[139] concurrent assert else, fileline:167
[141] concurrent assert else, fileline:167
[143] concurrent assert stmt, fileline:166
[145] concurrent assert else, fileline:167
[147] concurrent assert else, fileline:167
[149] concurrent assert else, fileline:167
[151] concurrent assert stmt, fileline:166
[153] concurrent assert else, fileline:167
[155] concurrent assert else, fileline:167
[157] concurrent assert else, fileline:167
[159] concurrent assert stmt, fileline:166
[161] concurrent assert else, fileline:167
[163] concurrent assert else, fileline:167
[165] concurrent assert else, fileline:167
[167] concurrent assert stmt, fileline:166
[169] concurrent assert else, fileline:167
[171] concurrent assert else, fileline:167
[173] concurrent assert else, fileline:167
[175] concurrent assert stmt, fileline:166
[177] concurrent assert else, fileline:167
[179] concurrent assert else, fileline:167
[181] concurrent assert else, fileline:167
[183] concurrent assert stmt, fileline:166
[185] concurrent assert else, fileline:167
[187] concurrent assert else, fileline:167
[189] concurrent assert else, fileline:167
[191] concurrent assert stmt, fileline:166
[193] concurrent assert else, fileline:167
[195] concurrent assert else, fileline:167
[197] concurrent assert else, fileline:167
[199] concurrent assert stmt, fileline:166
[2] triggered e1
[4] triggered e1
[4] single delay with const stmt, fileline:99
[6] triggered e1
[6] single delay with const stmt, fileline:99
[8] triggered e2
[10] triggered e2
[10] single delay with var stmt, fileline:102
[12] triggered e2
[12] single delay with var else, fileline:103
[14] triggered e2
[14] single delay with var stmt, fileline:102
[16] triggered e2
[16] single delay with var else, fileline:103
[18] triggered e2
[18] single delay with var stmt, fileline:102
[20] triggered e3
[22] triggered e3
[22] stmt1, fileline:106
[22] stmt2, fileline:107
[24] triggered e3
[24] else1, fileline:110
[24] else2, fileline:111
[26] triggered e3
[26] stmt1, fileline:106
[26] stmt2, fileline:107
[28] triggered e3
[28] else1, fileline:110
[28] else2, fileline:111
[30] triggered e4
[32] triggered e4
[34] triggered e4
[34] single multi-cycle delay with var stmt, fileline:115
[36] triggered e4
[36] single multi-cycle delay with var else, fileline:116
[38] triggered e4
[38] single multi-cycle delay with var stmt, fileline:115
[40] triggered e5
[42] triggered e5
[42] single delay with var brackets 1 stmt, fileline:119
[44] triggered e5
[44] single delay with var brackets 1 else, fileline:120
[46] triggered e5
[46] single delay with var brackets 1 stmt, fileline:119
[48] triggered e5
[48] single delay with var brackets 1 else, fileline:120
[50] triggered e6
[52] triggered e6
[52] single delay with var brackets 2 else, fileline:124
[54] triggered e6
[54] single delay with var brackets 2 stmt, fileline:123
[56] triggered e6
[56] single delay with var brackets 2 else, fileline:124
[58] triggered e6
[58] single delay with var brackets 2 stmt, fileline:123
[60] triggered e7
[62] triggered e7
[62] single delay with and var stmt, fileline:127
[64] triggered e7
[64] single delay with and var else, fileline:128
[66] triggered e7
[66] single delay with and var else, fileline:128
[68] triggered e7
[68] single delay with and var else, fileline:128
[70] triggered e8
[72] triggered e8
[72] single delay with negated var stmt, fileline:131
[74] triggered e8
[74] single delay with negated var else, fileline:132
[76] triggered e8
[76] single delay with negated var stmt, fileline:131
[78] triggered e8
[78] single delay with negated var else, fileline:132
[80] triggered e9
[82] triggered e9
[82] single delay with negated var brackets else, fileline:137
[84] triggered e9
[84] single delay with negated var brackets stmt, fileline:135
[86] triggered e9
[86] single delay with negated var brackets else, fileline:137
[88] triggered e9
[88] single delay with negated var brackets stmt, fileline:135
[90] triggered e10
[92] triggered e10
[94] triggered e10
[94] single delay with negated var brackets else, fileline:141
[96] triggered e10
[98] triggered e10
[98] single delay with negated var brackets else, fileline:141
[100] triggered e11
[102] triggered e11
[102] single delay with nested not stmt, fileline:144
[104] triggered e11
[104] single delay with nested not else, fileline:145
[106] triggered e11
[106] single delay with nested not stmt, fileline:144
[108] triggered e11
[108] single delay with nested not else, fileline:145
[110] triggered e12
[112] triggered e12
[114] triggered e12
[114] stmt, fileline: 148
[116] triggered e12
[116] else, fileline: 150
[118] triggered e12
[118] stmt, fileline: 148
[120] triggered e13
[122] triggered e13
[122] property, fileline:161
[124] triggered e13
[124] property, fileline:160
[126] triggered e13
[126] property, fileline:161
[128] triggered e13
[128] property, fileline:160
[130] triggered e14
[132] triggered e14
[132] else, fileline: 166
[134] triggered e14
[134] stmt, fileline: 164
[136] triggered e14
[136] else, fileline: 166
[138] triggered e14
[138] else, fileline: 166
[140] triggered e15
[140] else, fileline: 171
[142] triggered e15
[144] triggered e15
[144] else, fileline: 171
[144] else, fileline: 171
[146] triggered e15
[148] triggered e15
[148] else, fileline: 171
*-* All Finished *-*

View File

@ -4,6 +4,9 @@
// without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
`define TRIGGER(e) ->e; $display("[%0t] triggered %s", $time, `STRINGIFY(e))
module t ( /*AUTOARG*/
// Inputs
clk
@ -11,7 +14,7 @@ module t ( /*AUTOARG*/
input clk;
bit [3:0] val = 0;
bit [1:0] val = 0;
event e1;
event e2;
event e3;
@ -24,92 +27,73 @@ module t ( /*AUTOARG*/
event e10;
event e11;
event e12;
event e13;
event e14;
event e15;
integer cyc = 1;
always @(negedge clk) begin
val <= 4'(cyc % 4);
if (cyc >= 0 && cyc <= 4) begin
->e1;
always @(posedge clk) begin
++val;
++cyc;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e1", $time);
$display("[%0t] cyc=%0d val=%0d", $time, cyc, val);
`endif
end
if (cyc >= 5 && cyc <= 10) begin
->e2;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e2", $time);
`endif
end
if (cyc >= 11 && cyc <= 15) begin
->e3;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e3", $time);
`endif
end
if (cyc >= 16 && cyc <= 20) begin
->e4;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e4", $time);
`endif
end
if (cyc >= 21 && cyc <= 25) begin
->e5;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e5", $time);
`endif
end
if (cyc >= 26 && cyc <= 30) begin
->e6;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e6", $time);
`endif
end
if (cyc >= 31 && cyc <= 35) begin
->e7;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e7", $time);
`endif
end
if (cyc >= 36 && cyc <= 40) begin
->e8;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e8", $time);
`endif
end
if (cyc >= 41 && cyc <= 45) begin
->e9;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e9", $time);
`endif
end
if (cyc >= 46 && cyc <= 50) begin
->e10;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e10", $time);
`endif
end
if (cyc >= 51 && cyc <= 55) begin
->e11;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e11", $time);
`endif
end
if (cyc >= 56 && cyc <= 60) begin
->e12;
`ifdef TEST_VERBOSE
$display("[%0t] triggered e12", $time);
`endif
end
`ifdef TEST_VERBOSE
$display("cyc=%0d val=%0d", cyc, val);
`endif
cyc <= cyc + 1;
if (cyc == 100) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(negedge clk) begin
if (cyc >= 0 && cyc <= 4) begin
`TRIGGER(e1);
end
if (cyc >= 5 && cyc <= 10) begin
`TRIGGER(e2);
end
if (cyc >= 11 && cyc <= 15) begin
`TRIGGER(e3);
end
if (cyc >= 16 && cyc <= 20) begin
`TRIGGER(e4);
end
if (cyc >= 21 && cyc <= 25) begin
`TRIGGER(e5);
end
if (cyc >= 26 && cyc <= 30) begin
`TRIGGER(e6);
end
if (cyc >= 31 && cyc <= 35) begin
`TRIGGER(e7);
end
if (cyc >= 36 && cyc <= 40) begin
`TRIGGER(e8);
end
if (cyc >= 41 && cyc <= 45) begin
`TRIGGER(e9);
end
if (cyc >= 46 && cyc <= 50) begin
`TRIGGER(e10);
end
if (cyc >= 51 && cyc <= 55) begin
`TRIGGER(e11);
end
if (cyc >= 56 && cyc <= 60) begin
`TRIGGER(e12);
end
if (cyc >= 61 && cyc <= 65) begin
`TRIGGER(e13);
end
if (cyc >= 66 && cyc <= 70) begin
`TRIGGER(e14);
end
if (cyc >= 71 && cyc <= 75) begin
`TRIGGER(e15);
end
end
assert property (@(e1) ##1 1);
assert property (@(e1) ##1 1)
$display("[%0t] single delay with const stmt, fileline:%0d", $time, `__LINE__);
@ -118,51 +102,71 @@ module t ( /*AUTOARG*/
$display("[%0t] single delay with var stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with var else, fileline:%0d", $time, `__LINE__);
assert property (@(e3) ##2 val[0])
assert property (@(e3) ##1 val[0]) begin
$display("[%0t] stmt1, fileline:%0d", $time, `__LINE__);
$display("[%0t] stmt2, fileline:%0d", $time, `__LINE__);
end
else begin
$display("[%0t] else1, fileline:%0d", $time, `__LINE__);
$display("[%0t] else2, fileline:%0d", $time, `__LINE__);
end
assert property (@(e4) ##2 val[0])
$display("[%0t] single multi-cycle delay with var stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single multi-cycle delay with var else, fileline:%0d", $time, `__LINE__);
assert property (@(e4) ##1 (val[0]))
assert property (@(e5) ##1 (val[0]))
$display("[%0t] single delay with var brackets 1 stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with var brackets 1 else, fileline:%0d", $time, `__LINE__);
assert property (@(e5) (##1 (val[0])))
assert property (@(e6) (##1 (val[0])))
$display("[%0t] single delay with var brackets 2 stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with var brackets 2 else, fileline:%0d", $time, `__LINE__);
assert property (@(e6) (##1 val[0] && val[1]))
assert property (@(e7) (##1 val[0] && val[1]))
$display("[%0t] single delay with and var stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with and var else, fileline:%0d", $time, `__LINE__);
assert property (@(e8) not not not ##1 val[0])
$display("[%0t] single delay with negated var stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with negated var else, fileline:%0d", $time, `__LINE__);
assert property (@(e7) not ##1 val[0])
$display("[%0t] single delay with negated var stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with negated var else, fileline:%0d", $time, `__LINE__);
assume property (@(e8) not (##1 val[0]))
assume property (@(e9) not (##1 val[0]))
$display("[%0t] single delay with negated var brackets stmt, fileline:%0d", $time, `__LINE__);
else
$display("[%0t] single delay with negated var brackets else, fileline:%0d", $time, `__LINE__);
assume property (@(e9) not (##1 val[0]))
assume property (@(e10) not (##1 val[0]))
else
$display("[%0t] single delay with negated var brackets else, fileline:%0d", $time, `__LINE__);
assert property (@(e10) not (not ##1 val[0]))
assert property (@(e11) not (not ##1 val[0]))
$display("[%0t] single delay with nested not stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] single delay with nested not else, fileline:%0d", $time, `__LINE__);
restrict property (@(e11) ##1 val[0]);
restrict property (@(e11) not ##1 val[0]);
assert property (@(e12) not (not ##2 val[0] && val[0]))
$display("[%0t] stmt, fileline:%d", $time, `__LINE__);
else
$display("[%0t] else, fileline:%d", $time, `__LINE__);
`ifdef VERILATOR
restrict property (@(e12) ##1 val[0]);
restrict property (@(e12) not ##1 val[0]);
`endif
property prop;
@(e12) not ##1 val[0]
@(e13) not ##1 val[0]
endproperty
assert property (prop) $display("[%0t] property, fileline:%0d", $time, `__LINE__);
else $display("[%0t] property, fileline:%0d", $time, `__LINE__);
assert property (@(posedge clk) not (not ##2 val[0] && val[1]))
$display("[%0t] concurrent assert stmt, fileline:%0d", $time, `__LINE__);
else $display("[%0t] concurrent assert else, fileline:%0d", $time, `__LINE__);
assert property (@(e14) val[0] ##2 val[1])
$display("[%0t] stmt, fileline:%d", $time, `__LINE__);
else
$display("[%0t] else, fileline:%d", $time, `__LINE__);
assert property (@(e15) val[0] ##1 val[1] ##1 val[0])
$display("[%0t] stmt, fileline:%d", $time, `__LINE__);
else
$display("[%0t] else, fileline:%d", $time, `__LINE__);
endmodule

View File

@ -1,6 +1,6 @@
%Error: t/t_property_sexpr_bad.v:20:39: Syntax error: unexpected 'not' in sequence expression
%Error: t/t_property_sexpr_bad.v:20:39: Unexpected 'not' in sequence expression context
: ... note: In instance 't'
20 | assert property (@(posedge clk) ##1 not val) $display("[%0t] single delay with negated var stmt, fileline:%d", $time, 20);
20 | assert property (@(posedge clk) ##1 not val);
| ^~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -12,10 +12,10 @@ module t ( /*AUTOARG*/
input clk;
bit val;
always @(negedge clk) begin
always @(posedge clk) begin
$write("*-* All Finished *-*\n");
$finish;
end
assert property (@(posedge clk) ##1 not val) $display("[%0t] single delay with negated var stmt, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) ##1 not val);
endmodule

View File

@ -0,0 +1,5 @@
# SystemC::Coverage-3
C 'ft/t_property_sexpr_cov.vl44n3tuserpagev_user/tocoverhtop.t' 2
C 'ft/t_property_sexpr_cov.vl47n3tuserpagev_user/tocoverhtop.t' 2
C 'ft/t_property_sexpr_cov.vl50n3tuserpagev_user/tocoverhtop.t' 3
C 'ft/t_property_sexpr_cov.vl53n3tuserpagev_user/tocoverhtop.t' 25

View File

@ -1,31 +1,33 @@
[4] cover property, fileline:45
[7] concurrent cover, fileline:51
[4] cover property, fileline:48
[7] concurrent cover, fileline:54
[8] cover property, fileline:45
[12] not cover property, fileline:48
[15] concurrent cover, fileline:51
[18] not cover property, fileline:48
[20] not cover property, fileline:48
[23] concurrent cover, fileline:51
[31] concurrent cover, fileline:51
[39] concurrent cover, fileline:51
[47] concurrent cover, fileline:51
[55] concurrent cover, fileline:51
[63] concurrent cover, fileline:51
[71] concurrent cover, fileline:51
[79] concurrent cover, fileline:51
[87] concurrent cover, fileline:51
[95] concurrent cover, fileline:51
[103] concurrent cover, fileline:51
[111] concurrent cover, fileline:51
[119] concurrent cover, fileline:51
[127] concurrent cover, fileline:51
[135] concurrent cover, fileline:51
[143] concurrent cover, fileline:51
[151] concurrent cover, fileline:51
[159] concurrent cover, fileline:51
[167] concurrent cover, fileline:51
[175] concurrent cover, fileline:51
[183] concurrent cover, fileline:51
[191] concurrent cover, fileline:51
[199] concurrent cover, fileline:51
[8] cover property, fileline:48
[12] not cover property, fileline:51
[15] concurrent cover, fileline:54
[18] not cover property, fileline:51
[20] not cover property, fileline:51
[23] concurrent cover, fileline:54
[31] concurrent cover, fileline:54
[39] concurrent cover, fileline:54
[47] concurrent cover, fileline:54
[55] concurrent cover, fileline:54
[63] concurrent cover, fileline:54
[71] concurrent cover, fileline:54
[79] concurrent cover, fileline:54
[87] concurrent cover, fileline:54
[95] concurrent cover, fileline:54
[103] concurrent cover, fileline:54
[111] concurrent cover, fileline:54
[119] concurrent cover, fileline:54
[127] concurrent cover, fileline:54
[135] concurrent cover, fileline:54
[143] concurrent cover, fileline:54
[151] concurrent cover, fileline:54
[159] concurrent cover, fileline:54
[167] concurrent cover, fileline:54
[175] concurrent cover, fileline:54
[183] concurrent cover, fileline:54
[191] concurrent cover, fileline:54
[199] concurrent cover, fileline:54
*-* All Finished *-*

View File

@ -14,5 +14,6 @@ test.scenarios('simulator')
test.compile(timing_loop=True, verilator_flags2=['--assert', '--timing', '--coverage-user'])
test.execute(expect_filename=test.golden_filename)
test.files_identical(test.obj_dir + "/coverage.dat", "t/t_property_sexpr_cov.dat.out")
test.passes()

View File

@ -41,6 +41,9 @@ module t ( /*AUTOARG*/
end
end
cover property (@(e1) ##1 val[0])
$display("[%0t] cover property, fileline:%0d", $time, `__LINE__);
cover property (@(e1) ##1 val[0])
$display("[%0t] cover property, fileline:%0d", $time, `__LINE__);

View File

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

View File

@ -0,0 +1,249 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
`define TRIGGER(e) ->e; $display("[cyc=%0d, val=%0d] triggered %s", cyc, val, `STRINGIFY(e))
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%p exp='h%p\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
module t ( /*AUTOARG*/
// Inputs
clk
);
input clk;
bit [1:0] val = 0;
event e1;
event e2;
event e3;
event e4;
event e5;
event e6;
event e7;
event e8;
event e9;
event e10;
event e11;
event e12;
integer cyc = 1;
typedef struct {
bit fails;
bit passs;
} result_t;
result_t results [int];
result_t expected [int];
localparam MAX = 66;
always @(posedge clk) begin
++val;
++cyc;
if (cyc == MAX + 1) begin
expected[6] = '{1, 0};
expected[7] = '{1, 0};
expected[8] = '{1, 1};
expected[11] = '{1, 0};
expected[12] = '{1, 0};
expected[13] = '{1, 0};
expected[15] = '{1, 0};
expected[16] = '{1, 1};
expected[17] = '{1, 0};
expected[20] = '{1, 0};
expected[21] = '{1, 0};
expected[23] = '{1, 0};
expected[24] = '{1, 0};
expected[25] = '{1, 0};
expected[27] = '{1, 1};
expected[29] = '{1, 0};
expected[30] = '{1, 0};
expected[32] = '{1, 0};
expected[33] = '{1, 0};
expected[35] = '{1, 0};
expected[36] = '{1, 0};
expected[39] = '{1, 1};
expected[40] = '{0, 1};
expected[41] = '{0, 1};
expected[43] = '{1, 0};
expected[44] = '{1, 0};
expected[45] = '{1, 0};
expected[48] = '{0, 1};
expected[49] = '{0, 1};
expected[51] = '{1, 1};
expected[52] = '{0, 1};
expected[54] = '{0, 1};
expected[55] = '{1, 1};
expected[56] = '{0, 1};
expected[58] = '{1, 0};
expected[59] = '{1, 1};
expected[60] = '{1, 0};
expected[62] = '{0, 1};
expected[63] = '{0, 1};
expected[64] = '{0, 1};
expected[66] = '{0, 1};
`checkh(results, expected);
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(negedge clk) begin
if (cyc >= 5 && cyc <= 9) begin
`TRIGGER(e1);
end
if (cyc >= 10 && cyc <= 18) begin
`TRIGGER(e2);
end
if (cyc >= 19 && cyc <= 27) begin
`TRIGGER(e3);
end
if (cyc >= 28 && cyc <= 30) begin
`TRIGGER(e4);
end
if (cyc >= 31 && cyc <= 33) begin
`TRIGGER(e5);
end
if (cyc >= 34 && cyc <= 36) begin
`TRIGGER(e6);
end
if (cyc >= 37 && cyc <= 41) begin
`TRIGGER(e7);
end
if (cyc >= 42 && cyc <= 46) begin
`TRIGGER(e8);
end
if (cyc >= 47 && cyc <= 51) begin
`TRIGGER(e9);
end
if (cyc >= 52 && cyc <= 56) begin
`TRIGGER(e10);
end
if (cyc >= 57 && cyc <= 61) begin
`TRIGGER(e11);
end
if (cyc >= 62 && cyc <= MAX) begin
`TRIGGER(e12);
end
end
assert property (@(e1) ##1 1 ##1 1);
assert property (@(e1) val == 0 ##1 val == 1 ##1 val == 2 ##1 val == 3)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e2) ##1 val == 1 ##2 val == 3)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e3) ##1 val == 1 ##2 val == 3 ##3 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
// Test failure at each step
assert property (@(e4) cyc == 28 ##1 cyc == 0 ##1 cyc == 30)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e5) cyc == 31 ##1 cyc == 32 ##1 cyc == 0)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e6) ##1 cyc == 34 ##1 cyc == 0)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e7) not ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e8) not not ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e9) not not not ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e10) not val == 0 ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e11) not not val == 0 ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
assert property (@(e12) not not not val == 0 ##1 val == 1 ##1 val == 2)
results[cyc].passs = 1;
else
results[cyc].fails = 1;
empty_body_tests empty_body_tests(.clk(clk));
endmodule
module empty_body_tests(input clk);
event e;
int cyc = 0;
bit[7:0] hit = 0;
always @(posedge clk) begin
++cyc;
if (cyc < 5) ->e;
else `checkd(hit, 'b1111111);
end
assert property (@(e) ##1 1 ##1 1);
assert property (@(e) 1 ##1 1 ##1 1);
assert property (@(e) 1 ##1 1);
assert property (@(e) ##1 1 ##1 1) begin
hit |= 'b1;
end
assert property (@(e) 1 ##1 1 ##1 1) begin
hit |= 'b10;
end
assert property (@(e) 1 ##1 1) begin
hit |= 'b100;
end
assert property (@(e) ##1 1 ##1 0) else begin
hit |= 'b1000;
end
assert property (@(e) ##1 0) else begin
hit |= 'b10000;
end
assert property (@(e) 1 ##1 1 ##1 0) else begin
hit |= 'b100000;
end
assert property (@(e) 1 ##1 0) else begin
hit |= 'b1000000;
end
endmodule

View File

@ -1,35 +1,23 @@
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:29:39: Unsupported: ## (in sequence expression)
29 | assert property (@(posedge clk) val ##1 val) $display("[%0t] var with single delay stmt, fileline:%d", $time, 29);
| ^~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:30:43: Unsupported: ## (in sequence expression)
30 | assert property (@(posedge clk) ##1 val ##2 val) $display("[%0t] sequence stmt, fileline:%d", $time, 30);
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:71:14: Unsupported: sequence match items
71 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:75:14: Unsupported: sequence match items
75 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
| ^
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:71:29: Unsupported: ## range cycle delay range expression
71 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:75:29: Unsupported: ## range cycle delay range expression
75 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:70:13: Unsupported: property variable declaration
70 | integer l_b;
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:74:13: Unsupported: property variable declaration
74 | integer l_b;
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:88:16: Unsupported: sequence match items
88 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:92:16: Unsupported: sequence match items
92 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
| ^
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:88:51: Unsupported: [-> boolean abbrev expression
88 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:92:51: Unsupported: [-> boolean abbrev expression
92 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:88:54: Unsupported: boolean abbrev (in sequence expression)
88 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:92:54: Unsupported: boolean abbrev (in sequence expression)
92 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
| ^
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:88:35: Unsupported: ## (in sequence expression)
88 | (count == 0, l_t = $realtime) ##1 (count == 7)[->1] |-> $realtime - l_t < 50.5;
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:86:14: Unsupported: property variable declaration
86 | realtime l_t;
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:90:14: Unsupported: property variable declaration
90 | realtime l_t;
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:92:31: Unsupported: ## (in sequence expression)
92 | assert property (@clk not a ##1 b);
| ^~
%Error: Exiting due to

View File

@ -1,35 +1,50 @@
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:32:41: Unsupported: Implication with sequence expression
32 | assert property (@(posedge clk) ##1 1 |-> 1) $display("[%0t] single delay with const implication stmt, fileline:%d", $time, 32);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:29:41: Unsupported: Implication with sequence expression
: ... note: In instance 't'
29 | assert property (@(posedge clk) ##1 1 |-> 1) $display("[%0t] single delay with const implication stmt, fileline:%d", $time, 29);
| ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:34:41: Unsupported: Implication with sequence expression
34 | assert property (@(posedge clk) ##1 1 |-> not (val)) $display("[%0t] single delay implication with negated var stmt, fileline:%d", $time, 34);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:31:41: Unsupported: Implication with sequence expression
: ... note: In instance 't'
31 | assert property (@(posedge clk) ##1 1 |-> not (val)) $display("[%0t] single delay implication with negated var stmt, fileline:%d", $time, 31);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:36:37: Unsupported: Implication with sequence expression
36 | assert property (@(posedge clk) 1 |-> ##1 val) $display("[%0t] single delay implication with negated var stmt, fileline:%d", $time, 36);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:33:37: Unsupported: Implication with sequence expression
: ... note: In instance 't'
33 | assert property (@(posedge clk) 1 |-> ##1 val) $display("[%0t] single delay implication with negated var stmt, fileline:%d", $time, 33);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:38:45: Unsupported: Implication with sequence expression
38 | assert property (@(posedge clk) (##1 val) |-> (not val)) $display("[%0t] single delay with negated implication stmt, fileline:%d", $time, 38);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:35:45: Unsupported: Implication with sequence expression
: ... note: In instance 't'
35 | assert property (@(posedge clk) (##1 val) |-> (not val)) $display("[%0t] single delay with negated implication stmt, fileline:%d", $time, 35);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:40:45: Unsupported: Implication with sequence expression
40 | assert property (@(posedge clk) ##1 (val) |-> not (val)) $display("[%0t] single delay with negated implication brackets stmt, fileline:%d", $time, 40);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:37:45: Unsupported: Implication with sequence expression
: ... note: In instance 't'
37 | assert property (@(posedge clk) ##1 (val) |-> not (val)) $display("[%0t] single delay with negated implication brackets stmt, fileline:%d", $time, 37);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:44:41: Unsupported: Implication with sequence expression
44 | assert property (@(posedge clk) ##1 1 |-> 0) $display("[%0t] disable iff with cond implication stmt, fileline:%d", $time, 44);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:41:41: Unsupported: Implication with sequence expression
: ... note: In instance 't'
41 | assert property (@(posedge clk) ##1 1 |-> 0) $display("[%0t] disable iff with cond implication stmt, fileline:%d", $time, 41);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:46:45: Unsupported: Implication with sequence expression
46 | assert property (@(posedge clk) (##1 val) |-> (##1 val)) $display("[%0t] two delays implication stmt, fileline:%d", $time, 46);
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:43:45: Unsupported: Implication with sequence expression
: ... note: In instance 't'
43 | assert property (@(posedge clk) (##1 val) |-> (##1 val)) $display("[%0t] two delays implication stmt, fileline:%d", $time, 43);
| ^~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:48:52: Unsupported: Disable iff with sequence expression
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:45:52: Unsupported: Disable iff with sequence expression
: ... note: In instance 't'
48 | assert property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 48);
45 | assert property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 45);
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:50:52: Unsupported: Disable iff with sequence expression
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:47:52: Unsupported: Disable iff with sequence expression
: ... note: In instance 't'
50 | assume property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 50);
47 | assume property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 47);
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:52:51: Unsupported: Disable iff with sequence expression
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:49:51: Unsupported: Disable iff with sequence expression
: ... note: In instance 't'
52 | cover property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 52);
49 | cover property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, 49);
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:52:37: Unsupported: Disable iff with sequence expression
: ... note: In instance 't'
52 | @(posedge clk) disable iff (cyc != 5) ##1 0;
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:56:18: Unsupported: Implication with sequence expression
: ... note: In instance 't'
56 | ##1 cyc == 4 |-> 1;
| ^~~
%Error: Exiting due to

View File

@ -25,10 +25,7 @@ module t ( /*AUTOARG*/
end
end
end
`ifdef PARSING_TIME
assert property (@(posedge clk) val ##1 val) $display("[%0t] var with single delay stmt, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) ##1 val ##2 val) $display("[%0t] sequence stmt, fileline:%d", $time, `__LINE__);
`else
assert property (@(posedge clk) ##1 1 |-> 1) $display("[%0t] single delay with const implication stmt, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) ##1 1 |-> not (val)) $display("[%0t] single delay implication with negated var stmt, fileline:%d", $time, `__LINE__);
@ -50,7 +47,14 @@ module t ( /*AUTOARG*/
assume property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, `__LINE__);
cover property (@(posedge clk) disable iff (cyc != 5) ##1 0) $display("[%0t] disable iff stmt, fileline:%d", $time, `__LINE__);
`endif
property prop_disableiff;
@(posedge clk) disable iff (cyc != 5) ##1 0;
endproperty
property prop_implication;
##1 cyc == 4 |-> 1;
endproperty
endmodule
// Test parsing only

View File

@ -7,9 +7,6 @@
: ... note: In instance 't'
27 | q.size < 5;
| ^~~~
%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:31:10: Global constraints ignored (unsupported)
31 | foo.x < y;
| ^
%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:15:13: Unsupported: random member variable with the type of the containing class
: ... note: In instance 't'
15 | rand Cls cls;

View File

@ -66,152 +66,131 @@
68 | ## [+] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:71:4: Unsupported: sequence
71 | sequence s_cycdelay_int;
71 | sequence s_cycdelay_id;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:72:9: Unsupported: ## (in sequence expression)
72 | a ## 1 b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:72:9: Unsupported: ## id cycle delay range expression
72 | a ## DELAY b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:74:4: Unsupported: sequence
74 | sequence s_cycdelay_id;
74 | sequence s_cycdelay_pid;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:75:9: Unsupported: ## id cycle delay range expression
75 | a ## DELAY b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:75:9: Unsupported: ## (in sequence expression)
75 | a ## DELAY b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:75:9: Unsupported: ## () cycle delay range expression
75 | a ## ( DELAY ) b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:77:4: Unsupported: sequence
77 | sequence s_cycdelay_pid;
77 | sequence s_cycdelay_range;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:78:9: Unsupported: ## () cycle delay range expression
78 | a ## ( DELAY ) b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:78:9: Unsupported: ## (in sequence expression)
78 | a ## ( DELAY ) b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:78:9: Unsupported: ## range cycle delay range expression
78 | a ## [1:2] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:80:4: Unsupported: sequence
80 | sequence s_cycdelay_range;
80 | sequence s_cycdelay_star;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:81:9: Unsupported: ## range cycle delay range expression
81 | a ## [1:2] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:81:9: Unsupported: ## (in sequence expression)
81 | a ## [1:2] b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:81:9: Unsupported: ## [*] cycle delay range expression
81 | a ## [*] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:83:4: Unsupported: sequence
83 | sequence s_cycdelay_star;
83 | sequence s_cycdelay_plus;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:84:9: Unsupported: ## [*] cycle delay range expression
84 | a ## [*] b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:84:9: Unsupported: ## [+] cycle delay range expression
84 | a ## [+] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:84:9: Unsupported: ## (in sequence expression)
84 | a ## [*] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:86:4: Unsupported: sequence
86 | sequence s_cycdelay_plus;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:87:4: Unsupported: sequence
87 | sequence s_booleanabbrev_brastar_int;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:87:9: Unsupported: ## [+] cycle delay range expression
87 | a ## [+] b;
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:87:9: Unsupported: ## (in sequence expression)
87 | a ## [+] b;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:88:9: Unsupported: [*] boolean abbrev expression
88 | a [* 1 ];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:88:12: Unsupported: boolean abbrev (in sequence expression)
88 | a [* 1 ];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:90:4: Unsupported: sequence
90 | sequence s_booleanabbrev_brastar_int;
90 | sequence s_booleanabbrev_brastar;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:91:9: Unsupported: [*] boolean abbrev expression
91 | a [* 1 ];
91 | a [*];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:91:9: Unsupported: boolean abbrev (in sequence expression)
91 | a [*];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:91:12: Unsupported: boolean abbrev (in sequence expression)
91 | a [* 1 ];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:93:4: Unsupported: sequence
93 | sequence s_booleanabbrev_brastar;
93 | sequence s_booleanabbrev_plus;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:94:9: Unsupported: [*] boolean abbrev expression
94 | a [*];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:94:9: Unsupported: [+] boolean abbrev expression
94 | a [+];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:94:9: Unsupported: boolean abbrev (in sequence expression)
94 | a [*];
| ^~
94 | a [+];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:96:4: Unsupported: sequence
96 | sequence s_booleanabbrev_plus;
96 | sequence s_booleanabbrev_eq;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:9: Unsupported: [+] boolean abbrev expression
97 | a [+];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:9: Unsupported: boolean abbrev (in sequence expression)
97 | a [+];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:9: Unsupported: [= boolean abbrev expression
97 | a [= 1];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:97:12: Unsupported: boolean abbrev (in sequence expression)
97 | a [= 1];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:99:4: Unsupported: sequence
99 | sequence s_booleanabbrev_eq;
99 | sequence s_booleanabbrev_eq_range;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:100:9: Unsupported: [= boolean abbrev expression
100 | a [= 1];
100 | a [= 1:2];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:100:12: Unsupported: boolean abbrev (in sequence expression)
100 | a [= 1];
100 | a [= 1:2];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:102:4: Unsupported: sequence
102 | sequence s_booleanabbrev_eq_range;
102 | sequence s_booleanabbrev_minusgt;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:9: Unsupported: [= boolean abbrev expression
103 | a [= 1:2];
| ^~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:12: Unsupported: boolean abbrev (in sequence expression)
103 | a [= 1:2];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:9: Unsupported: [-> boolean abbrev expression
103 | a [-> 1];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:103:13: Unsupported: boolean abbrev (in sequence expression)
103 | a [-> 1];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:105:4: Unsupported: sequence
105 | sequence s_booleanabbrev_minusgt;
105 | sequence s_booleanabbrev_minusgt_range;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:106:9: Unsupported: [-> boolean abbrev expression
106 | a [-> 1];
106 | a [-> 1:2];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:106:13: Unsupported: boolean abbrev (in sequence expression)
106 | a [-> 1];
106 | a [-> 1:2];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:108:4: Unsupported: sequence
108 | sequence s_booleanabbrev_minusgt_range;
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:4: Unsupported: sequence
109 | sequence p_arg_seqence(sequence inseq);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:9: Unsupported: [-> boolean abbrev expression
109 | a [-> 1:2];
| ^~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:13: Unsupported: boolean abbrev (in sequence expression)
109 | a [-> 1:2];
| ^
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:112:4: Unsupported: sequence
112 | sequence p_arg_seqence(sequence inseq);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:112:27: Unsupported: sequence argument data type
112 | sequence p_arg_seqence(sequence inseq);
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:109:27: Unsupported: sequence argument data type
109 | sequence p_arg_seqence(sequence inseq);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:113:4: Unsupported: sequence
113 | sequence s_firstmatch_a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:114:7: Unsupported: first_match (in sequence expression)
114 | first_match (a);
| ^~~~~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:116:4: Unsupported: sequence
116 | sequence s_firstmatch_a;
116 | sequence s_firstmatch_ab;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:117:7: Unsupported: first_match (in sequence expression)
117 | first_match (a);
117 | first_match (a, res0 = 1);
| ^~~~~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:119:4: Unsupported: sequence
119 | sequence s_firstmatch_ab;
119 | sequence s_firstmatch_abc;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:120:7: Unsupported: first_match (in sequence expression)
120 | first_match (a, res0 = 1);
120 | first_match (a, res0 = 1, res1 = 2);
| ^~~~~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:122:4: Unsupported: sequence
122 | sequence s_firstmatch_abc;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:123:7: Unsupported: first_match (in sequence expression)
123 | first_match (a, res0 = 1, res1 = 2);
| ^~~~~~~~~~~
%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:126:10: Ignoring unsupported: cover sequence
126 | cover sequence (s_a) $display("");
%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:123:10: Ignoring unsupported: cover sequence
123 | cover sequence (s_a) $display("");
| ^~~~~~~~
... 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_sequence_sexpr_unsup.v:127:10: Ignoring unsupported: cover sequence
127 | cover sequence (@(posedge a) disable iff (b) s_a) $display("");
%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:124:10: Ignoring unsupported: cover sequence
124 | cover sequence (@(posedge a) disable iff (b) s_a) $display("");
| ^~~~~~~~
%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:128:10: Ignoring unsupported: cover sequence
128 | cover sequence (disable iff (b) s_a) $display("");
%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:125:10: Ignoring unsupported: cover sequence
125 | cover sequence (disable iff (b) s_a) $display("");
| ^~~~~~~~
%Error: Exiting due to

View File

@ -68,9 +68,6 @@ module t (/*AUTOARG*/
## [+] b;
endsequence
sequence s_cycdelay_int;
a ## 1 b;
endsequence
sequence s_cycdelay_id;
a ## DELAY b;
endsequence

View File

@ -0,0 +1 @@
t_sys_file_basic.out

View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("simulator")
test.top_filename = "t/t_sys_file_basic.v"
test.unlink_ok(test.obj_dir + "/t_sys_file_basic_test.log")
test.compile(
verilator_flags2=["--coverage-expr"],
# Build without cached objects, see bug363
make_flags=["VM_PARALLEL_BUILDS=0"],
)
test.execute()
test.files_identical(test.obj_dir + "/t_sys_file_basic_test.log", test.golden_filename)
test.passes()