2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Collect and print statistics
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2005-2025 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Assert.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Stats.h"
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2025-08-24 03:16:53 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// AssertDeFuture
|
|
|
|
|
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
|
|
|
|
// see IEEE 1800-2023 16.9.4.
|
|
|
|
|
|
|
|
|
|
class AssertDeFuture final : public VNVisitor {
|
|
|
|
|
// STATE - across all visitors
|
|
|
|
|
AstNodeModule* const m_modp; // Module future is underneath
|
|
|
|
|
const AstFuture* m_futurep; // First AstFuture found
|
|
|
|
|
const unsigned m_pastNum; // Prefix unique number for this module
|
|
|
|
|
std::map<AstVar*, AstVar*> m_delayedVars; // Old to delayed variable mapping
|
|
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
|
|
|
|
bool m_inFuture = false; // Inside a future
|
|
|
|
|
bool m_unsupported = false; // Printed unsupported
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void unsupported(AstNode* nodep) {
|
|
|
|
|
if (m_unsupported) return;
|
|
|
|
|
m_unsupported = true;
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
2025-10-14 02:30:47 +02:00
|
|
|
"Unsupported/illegal: Future value function used with expression with "
|
2025-08-24 03:16:53 +02:00
|
|
|
<< nodep->prettyOperatorName());
|
|
|
|
|
}
|
|
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstFuture* nodep) override {
|
|
|
|
|
VL_RESTORER(m_inFuture);
|
|
|
|
|
m_inFuture = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
// Done with the future, this subexpression is current-time
|
|
|
|
|
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
if (m_inFuture || m_unsupported)
|
|
|
|
|
return; // Need user1 set above, don't process when Future is removed
|
|
|
|
|
if (nodep->access().isWriteOrRW()) {
|
|
|
|
|
unsupported(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto it = m_delayedVars.find(nodep->varp());
|
|
|
|
|
AstVar* outvarp;
|
|
|
|
|
if (it == m_delayedVars.end()) {
|
|
|
|
|
AstSenTree* const sentreep = m_futurep->sentreep();
|
|
|
|
|
AstAlways* const alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS,
|
|
|
|
|
sentreep->cloneTree(false), nullptr};
|
|
|
|
|
m_modp->addStmtsp(alwaysp);
|
|
|
|
|
outvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
"__Vnotfuture" + cvtToStr(m_pastNum) + "_" + nodep->name(),
|
|
|
|
|
nodep->dtypep()};
|
|
|
|
|
m_modp->addStmtsp(outvarp);
|
|
|
|
|
AstVarRef* varRefAWritep = new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE};
|
|
|
|
|
varRefAWritep->user1(true);
|
|
|
|
|
AstNodeVarRef* varRefAReadp = nodep->cloneTree(false);
|
|
|
|
|
varRefAReadp->user1(true);
|
|
|
|
|
AstNode* const assp = new AstAssignDly{nodep->fileline(), varRefAWritep, varRefAReadp};
|
|
|
|
|
alwaysp->addStmtsp(assp);
|
|
|
|
|
m_delayedVars.emplace(nodep->varp(), outvarp);
|
|
|
|
|
} else {
|
|
|
|
|
outvarp = it->second;
|
|
|
|
|
}
|
|
|
|
|
AstVarRef* newp = new AstVarRef{nodep->fileline(), outvarp, VAccess::READ};
|
|
|
|
|
newp->user1(true);
|
|
|
|
|
UINFO(9, "DeFuture " << nodep << " becomes " << newp);
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeFTaskRef* nodep) override { unsupported(nodep); }
|
|
|
|
|
void visit(AstMethodCall* nodep) override { unsupported(nodep); }
|
|
|
|
|
void visit(AstNode* nodep) override {
|
|
|
|
|
if (!nodep->isPure()) unsupported(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
explicit AssertDeFuture(AstNode* nodep, AstNodeModule* modp, unsigned pastNum)
|
|
|
|
|
: m_modp{modp}
|
|
|
|
|
, m_pastNum{pastNum} {
|
|
|
|
|
// See if any Future before we process
|
|
|
|
|
if (nodep->forall([&](const AstFuture* futurep) -> bool {
|
|
|
|
|
m_futurep = futurep;
|
|
|
|
|
return false;
|
|
|
|
|
}))
|
|
|
|
|
return;
|
|
|
|
|
// UINFOTREE(9, nodep, "", "defuture-in");
|
|
|
|
|
visit(nodep); // Nodep may get deleted
|
|
|
|
|
// UINFOTREE(9, nodep, "", "defuture-ou");
|
|
|
|
|
}
|
|
|
|
|
~AssertDeFuture() = default;
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2025-08-24 03:10:46 +02:00
|
|
|
// AssertVisitor
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class AssertVisitor final : public VNVisitor {
|
2024-08-05 23:54:13 +02:00
|
|
|
// CONSTANTS
|
|
|
|
|
static constexpr uint8_t ALL_ASSERT_TYPES
|
|
|
|
|
= std::numeric_limits<std::underlying_type<VAssertType::en>::type>::max();
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE/TYPES
|
|
|
|
|
// Cleared on netlist
|
2025-08-24 03:16:53 +02:00
|
|
|
// AstNode::user1() -> bool. True if processed
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Last module
|
2025-09-23 20:49:01 +02:00
|
|
|
const AstNode* m_beginp = nullptr; // Last AstBegin/AstGenBlock
|
2020-11-29 17:31:38 +01:00
|
|
|
unsigned m_monitorNum = 0; // Global $monitor numbering (not per module)
|
|
|
|
|
AstVar* m_monitorNumVarp = nullptr; // $monitor number variable
|
|
|
|
|
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
|
2020-08-16 15:55:36 +02:00
|
|
|
unsigned m_modPastNum = 0; // Module past numbering
|
2020-11-29 17:31:38 +01:00
|
|
|
unsigned m_modStrobeNum = 0; // Module $strobe numbering
|
2023-02-28 06:35:37 +01:00
|
|
|
const AstNodeProcedure* m_procedurep = nullptr; // Current procedure
|
2020-04-14 04:51:35 +02:00
|
|
|
VDouble0 m_statCover; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAsNotImm; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAsImm; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAsFull; // Statistic tracking
|
2025-08-24 03:10:46 +02:00
|
|
|
VDouble0 m_statPastVars; // Statistic tracking
|
2022-08-29 14:39:41 +02:00
|
|
|
bool m_inSampled = false; // True inside a sampled expression
|
2025-10-10 16:16:15 +02:00
|
|
|
bool m_inRestrict = false; // True inside restrict assertion
|
2025-11-06 14:42:27 +01:00
|
|
|
AstNode* m_passsp = nullptr; // Current pass statement
|
|
|
|
|
AstNode* m_failsp = nullptr; // Current fail statement
|
|
|
|
|
bool m_underAssert = false; // Visited from assert
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2024-08-05 23:54:13 +02:00
|
|
|
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
|
|
|
|
|
VAssertDirectiveType directiveType) {
|
2025-06-28 18:29:41 +02:00
|
|
|
// cppcheck-suppress missingReturn
|
2024-08-05 23:54:13 +02:00
|
|
|
switch (directiveType) {
|
|
|
|
|
case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
|
|
|
|
|
case VAssertDirectiveType::VIOLATION_CASE: {
|
2025-07-04 01:36:28 +02:00
|
|
|
if (v3Global.opt.assertCase()) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
|
|
|
|
|
1};
|
2024-08-05 23:54:13 +02:00
|
|
|
}
|
|
|
|
|
// If assertions are off, have constant propagation rip them out later
|
|
|
|
|
// This allows syntax errors and such to be detected normally.
|
|
|
|
|
return new AstConst{fl, AstConst::BitFalse{}};
|
|
|
|
|
}
|
|
|
|
|
case VAssertDirectiveType::ASSERT:
|
|
|
|
|
case VAssertDirectiveType::COVER:
|
|
|
|
|
case VAssertDirectiveType::ASSUME: {
|
|
|
|
|
if (v3Global.opt.assertOn()) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
return new AstCExpr{fl, AstCExpr::Pure{},
|
2024-08-05 23:54:13 +02:00
|
|
|
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
|
|
|
|
|
+ ", "s + std::to_string(directiveType) + ")"s,
|
|
|
|
|
1};
|
|
|
|
|
}
|
|
|
|
|
return new AstConst{fl, AstConst::BitFalse{}};
|
|
|
|
|
}
|
|
|
|
|
case VAssertDirectiveType::INTERNAL:
|
|
|
|
|
case VAssertDirectiveType::VIOLATION_IF:
|
|
|
|
|
case VAssertDirectiveType::RESTRICT: {
|
|
|
|
|
if (v3Global.opt.assertOn()) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
|
|
|
|
|
1};
|
2024-08-05 23:54:13 +02:00
|
|
|
}
|
|
|
|
|
return new AstConst{fl, AstConst::BitFalse{}};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VL_UNREACHABLE;
|
2024-02-23 15:05:53 +01:00
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
|
2023-03-25 00:22:48 +01:00
|
|
|
VDisplayType severity) {
|
|
|
|
|
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
2024-07-14 17:39:45 +02:00
|
|
|
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
2023-03-25 00:22:48 +01:00
|
|
|
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
|
|
|
|
+ ((message != "") ? ": " : "") + message + "\n");
|
|
|
|
|
} else {
|
2024-07-14 17:39:45 +02:00
|
|
|
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
2023-03-25 00:22:48 +01:00
|
|
|
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
|
|
|
|
|
+ ((message != "") ? ": " : "") + message + "\n");
|
|
|
|
|
}
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2024-07-10 11:06:13 +02:00
|
|
|
static bool resolveAssertType(AstAssertCtl* nodep) {
|
|
|
|
|
if (!nodep->assertTypesp()) {
|
2024-08-05 23:54:13 +02:00
|
|
|
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
2024-07-10 11:06:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
|
2024-08-05 23:54:13 +02:00
|
|
|
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
|
2024-07-10 11:06:13 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
static bool resolveControlType(AstAssertCtl* nodep) {
|
|
|
|
|
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
|
|
|
|
nodep->ctlType(constp->toSInt());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-08-05 23:54:13 +02:00
|
|
|
static bool resolveDirectiveType(AstAssertCtl* nodep) {
|
|
|
|
|
if (!nodep->directiveTypesp()) {
|
|
|
|
|
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
|
|
|
|
| VAssertDirectiveType::COVER);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
|
|
|
|
|
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2007-03-06 22:43:38 +01:00
|
|
|
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
2023-03-25 00:22:48 +01:00
|
|
|
nodep->fmtp()->text(
|
|
|
|
|
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
2022-01-02 19:56:40 +01:00
|
|
|
nodep->displayType(VDisplayType::DT_WRITE);
|
2019-05-19 22:13:13 +02:00
|
|
|
// cppcheck-suppress nullPointer
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
|
|
|
|
|
if (AstNodeExpr* const timesp = nodep->fmtp()->exprsp()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
timesp->unlinkFrBackWithNext();
|
|
|
|
|
timenewp->addNext(timesp);
|
|
|
|
|
}
|
|
|
|
|
nodep->fmtp()->addExprsp(timenewp);
|
|
|
|
|
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
2021-11-27 02:38:48 +01:00
|
|
|
nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
|
2025-03-02 23:01:35 +01:00
|
|
|
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep};
|
2022-08-29 14:39:41 +02:00
|
|
|
sampledp->dtypeFrom(nodep);
|
|
|
|
|
return sampledp;
|
|
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
|
2020-11-29 17:31:38 +01:00
|
|
|
if (!m_monitorNumVarp) {
|
2022-01-02 19:56:40 +01:00
|
|
|
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
|
|
|
|
|
nodep->findUInt64DType()};
|
2022-09-15 20:43:56 +02:00
|
|
|
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
|
2020-11-29 17:31:38 +01:00
|
|
|
}
|
2025-03-02 23:01:35 +01:00
|
|
|
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
|
2020-11-29 17:31:38 +01:00
|
|
|
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
|
|
|
|
return varrefp;
|
|
|
|
|
}
|
2025-08-19 23:02:10 +02:00
|
|
|
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
|
2020-11-29 17:31:38 +01:00
|
|
|
if (!m_monitorOffVarp) {
|
2022-01-02 19:56:40 +01:00
|
|
|
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
|
|
|
|
|
nodep->findBitDType()};
|
2022-09-15 20:43:56 +02:00
|
|
|
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
|
2020-11-29 17:31:38 +01:00
|
|
|
}
|
2025-03-02 23:01:35 +01:00
|
|
|
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
|
2020-11-29 17:31:38 +01:00
|
|
|
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
|
|
|
|
return varrefp;
|
|
|
|
|
}
|
2024-08-05 23:54:13 +02:00
|
|
|
static AstNodeStmt* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
|
|
|
|
VAssertType type = VAssertType::INTERNAL) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Add a internal if to check assertions are on.
|
|
|
|
|
// Don't make this a AND term, as it's unlikely to need to test this.
|
2024-07-10 11:06:13 +02:00
|
|
|
FileLine* const fl = bodyp->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
|
2024-08-05 23:54:13 +02:00
|
|
|
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
|
2024-07-10 11:06:13 +02:00
|
|
|
AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
|
2022-10-22 01:10:06 +02:00
|
|
|
newp->isBoundsCheck(true); // To avoid LATCH warning
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->user1(true); // Don't assert/cover this if
|
|
|
|
|
return newp;
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-10 16:16:15 +02:00
|
|
|
static AstIf* assertCond(AstNodeCoverOrAssert* nodep, AstNodeExpr* propp, AstNode* passsp,
|
|
|
|
|
AstNode* failsp) {
|
|
|
|
|
|
|
|
|
|
AstIf* const ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
|
|
|
|
|
// It's more LIKELY that we'll take the nullptr if clause
|
|
|
|
|
// than the sim-killing else clause:
|
|
|
|
|
ifp->branchPred(VBranchPred::BP_LIKELY);
|
|
|
|
|
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
|
|
|
|
return ifp;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-06 14:42:27 +01:00
|
|
|
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);
|
2025-10-10 16:16:15 +02:00
|
|
|
}
|
2025-11-06 14:42:27 +01:00
|
|
|
return newIfAssertOn(bodyp, nodep->directive(), nodep->type());
|
2025-10-10 16:16:15 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
|
2024-07-10 11:06:13 +02:00
|
|
|
AstNodeExpr* exprsp = nullptr) {
|
2018-03-11 15:37:20 +01:00
|
|
|
// Like newFireAssert() but omits the asserts-on check
|
2022-01-02 19:56:40 +01:00
|
|
|
AstDisplay* const dispp
|
2022-10-22 01:03:40 +02:00
|
|
|
= new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
|
2021-05-11 15:38:13 +02:00
|
|
|
dispp->fmtp()->timeunit(m_modp->timeunit());
|
2024-07-10 11:06:13 +02:00
|
|
|
AstNodeStmt* const bodysp = dispp;
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
2024-02-13 13:53:32 +01:00
|
|
|
if (exprsp) dispp->fmtp()->exprsp()->addNext(exprsp);
|
2024-09-14 02:45:44 +02:00
|
|
|
if (v3Global.opt.stopFail()) bodysp->addNext(new AstStop{nodep->fileline(), false});
|
2018-03-11 15:37:20 +01:00
|
|
|
return bodysp;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
AstNodeStmt* newFireAssert(const AstNodeStmt* nodep, VAssertDirectiveType directiveType,
|
2024-08-05 23:54:13 +02:00
|
|
|
VAssertType assertType, const string& message,
|
2024-07-10 11:06:13 +02:00
|
|
|
AstNodeExpr* exprsp = nullptr) {
|
|
|
|
|
AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
|
|
|
|
|
bodysp = newIfAssertOn(bodysp, directiveType, assertType);
|
2019-05-19 22:13:13 +02:00
|
|
|
return bodysp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-24 03:16:53 +02:00
|
|
|
void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
|
2019-12-17 03:43:52 +01:00
|
|
|
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
|
|
|
|
|
2025-08-24 03:16:53 +02:00
|
|
|
{ AssertDeFuture{nodep->propp(), m_modp, m_modPastNum++}; }
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstSenTree* const sentreep = nodep->sentreep();
|
2019-12-17 03:43:52 +01:00
|
|
|
const string& message = nodep->name();
|
|
|
|
|
AstNode* passsp = nodep->passsp();
|
|
|
|
|
if (passsp) passsp->unlinkFrBackWithNext();
|
|
|
|
|
if (failsp) failsp->unlinkFrBackWithNext();
|
|
|
|
|
|
|
|
|
|
if (nodep->immediate()) {
|
2020-01-25 02:10:44 +01:00
|
|
|
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
|
2019-12-17 03:43:52 +01:00
|
|
|
} else {
|
2020-01-25 02:10:44 +01:00
|
|
|
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
|
2019-12-17 03:43:52 +01:00
|
|
|
sentreep->unlinkFrBack();
|
2023-02-28 06:35:37 +01:00
|
|
|
if (m_procedurep) {
|
|
|
|
|
// To support this need queue of asserts to activate
|
2025-09-20 14:19:42 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Procedural concurrent assertion with"
|
|
|
|
|
" clocking event inside always (IEEE 1800-2023 16.14.6)");
|
2023-02-28 06:35:37 +01:00
|
|
|
}
|
2019-12-17 03:43:52 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
bool selfDestruct = false;
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
2019-12-17 03:43:52 +01:00
|
|
|
++m_statCover;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!v3Global.opt.coverageUser()) {
|
|
|
|
|
selfDestruct = true;
|
|
|
|
|
} else {
|
|
|
|
|
// V3Coverage assigned us a bucket to increment.
|
2022-09-15 20:43:56 +02:00
|
|
|
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
2019-10-27 14:27:18 +01:00
|
|
|
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
2020-04-14 04:51:35 +02:00
|
|
|
if (message != "") covincp->declp()->comment(message);
|
2025-10-10 16:16:15 +02:00
|
|
|
if (passsp) {
|
|
|
|
|
passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
|
|
|
|
|
} else {
|
|
|
|
|
passsp = covincp;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-12-06 04:58:36 +01:00
|
|
|
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
|
2020-04-14 04:51:35 +02:00
|
|
|
if (nodep->immediate()) {
|
|
|
|
|
++m_statAsImm;
|
|
|
|
|
} else {
|
|
|
|
|
++m_statAsNotImm;
|
|
|
|
|
}
|
2023-03-11 04:13:17 +01:00
|
|
|
if (!passsp && !failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Unknown node type");
|
|
|
|
|
}
|
2018-03-11 15:37:20 +01:00
|
|
|
|
2025-11-06 14:42:27 +01:00
|
|
|
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);
|
2025-10-10 16:16:15 +02:00
|
|
|
if (sentreep) {
|
|
|
|
|
bodysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 23:39:44 +02:00
|
|
|
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
|
|
|
|
|
if (failsp && !failsp->backp()) VL_DO_DANGLING(pushDeletep(failsp), failsp);
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Install it
|
|
|
|
|
if (selfDestruct) {
|
|
|
|
|
// Delete it after making the tree. This way we can tell the user
|
|
|
|
|
// if it wasn't constructed nicely or has other errors without needing --coverage.
|
2025-10-10 16:16:15 +02:00
|
|
|
VL_DO_DANGLING(bodysp->deleteTree(), bodysp);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
} else {
|
2025-10-10 16:16:15 +02:00
|
|
|
nodep->replaceWith(bodysp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Bye
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-23 21:09:47 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstIf* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstNodeIf* ifp = nodep;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* propp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
bool hasDefaultElse = false;
|
|
|
|
|
do {
|
|
|
|
|
// If this statement ends with 'else if', then nextIf will point to the
|
|
|
|
|
// nextIf statement. Otherwise it will be null.
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstNodeIf* const nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(ifp->condp());
|
2014-03-17 02:38:29 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Recurse into the true case.
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(ifp->thensp());
|
2017-09-12 01:18:58 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// If the last else is not an else if, recurse into that too.
|
2020-04-14 04:51:35 +02:00
|
|
|
if (ifp->elsesp() && !nextifp) { //
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(ifp->elsesp());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build a bitmask of the true predicates
|
2023-09-17 04:50:54 +02:00
|
|
|
AstNodeExpr* const predp = ifp->condp()->cloneTreePure(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (propp) {
|
2022-10-22 01:03:40 +02:00
|
|
|
propp = new AstConcat{nodep->fileline(), predp, propp};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
propp = predp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record if this ends with an 'else' that does not have an if
|
2020-04-14 04:51:35 +02:00
|
|
|
if (ifp->elsesp() && !nextifp) hasDefaultElse = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
ifp = nextifp;
|
|
|
|
|
} while (ifp);
|
|
|
|
|
|
2023-09-09 03:51:59 +02:00
|
|
|
AstIf* const newifp = nodep->cloneTree(false);
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool allow_none = nodep->unique0Pragma();
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Empty case means no property
|
2022-10-22 01:03:40 +02:00
|
|
|
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
|
|
|
|
// predicates evaluates to true.
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const ohot
|
2021-11-13 19:50:44 +01:00
|
|
|
= ((allow_none || hasDefaultElse)
|
2022-11-13 21:33:11 +01:00
|
|
|
? static_cast<AstNodeExpr*>(new AstOneHot0{nodep->fileline(), propp})
|
|
|
|
|
: static_cast<AstNodeExpr*>(new AstOneHot{nodep->fileline(), propp}));
|
2024-08-05 23:54:13 +02:00
|
|
|
const VAssertType assertType
|
2024-07-10 11:06:13 +02:00
|
|
|
= nodep->uniquePragma() ? VAssertType::UNIQUE : VAssertType::UNIQUE0;
|
|
|
|
|
AstIf* const checkifp
|
|
|
|
|
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
|
2024-08-05 23:54:13 +02:00
|
|
|
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_IF, assertType,
|
2024-07-10 11:06:13 +02:00
|
|
|
"'unique if' statement violated"),
|
|
|
|
|
newifp};
|
2022-10-22 01:10:06 +02:00
|
|
|
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
2019-10-05 13:54:14 +02:00
|
|
|
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(checkifp);
|
2024-05-08 14:36:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2014-03-17 02:38:29 +01:00
|
|
|
}
|
2007-03-06 22:43:38 +01:00
|
|
|
|
2018-09-23 21:09:47 +02:00
|
|
|
//========== Case assertions
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCase* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1SetOnce()) {
|
|
|
|
|
bool has_default = false;
|
2020-04-14 04:51:35 +02:00
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (itemp->isDefault()) has_default = true;
|
|
|
|
|
}
|
2024-02-22 09:09:14 +01:00
|
|
|
const AstNodeDType* exprDtypep = nodep->exprp()->dtypep()->skipRefp();
|
2024-07-10 11:06:13 +02:00
|
|
|
|
2024-08-05 23:54:13 +02:00
|
|
|
VAssertType assertType = VAssertType::INTERNAL;
|
2024-07-10 11:06:13 +02:00
|
|
|
if (nodep->priorityPragma()) {
|
|
|
|
|
assertType = VAssertType::PRIORITY;
|
|
|
|
|
} else if (nodep->uniquePragma()) {
|
|
|
|
|
assertType = VAssertType::UNIQUE;
|
|
|
|
|
} else if (nodep->unique0Pragma()) {
|
|
|
|
|
assertType = VAssertType::UNIQUE0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 09:09:14 +01:00
|
|
|
string valFmt;
|
|
|
|
|
if (exprDtypep->isIntegralOrPacked())
|
|
|
|
|
valFmt = " for '" + cvtToStr(exprDtypep->widthMin()) + "'h%X'";
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
2022-03-31 02:17:59 +02:00
|
|
|
// Need to add a default if there isn't one already
|
2019-05-19 22:13:13 +02:00
|
|
|
++m_statAsFull;
|
|
|
|
|
if (!has_default) {
|
2022-10-22 01:03:40 +02:00
|
|
|
nodep->addItemsp(new AstCaseItem{
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep->fileline(), nullptr /*DEFAULT*/,
|
2024-08-05 23:54:13 +02:00
|
|
|
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
2024-02-23 15:05:53 +01:00
|
|
|
nodep->pragmaString() + ", but non-match found" + valFmt,
|
|
|
|
|
valFmt.empty() ? nullptr
|
|
|
|
|
: nodep->exprp()->cloneTreePure(false))});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
|
|
|
|
// Need to check that one, and only one of the case items match at any moment
|
|
|
|
|
// If there's a default, we allow none to match, else exactly one must match
|
|
|
|
|
++m_statAsFull;
|
|
|
|
|
if (!has_default && !nodep->itemsp()) {
|
|
|
|
|
// Not parallel, but harmlessly so.
|
|
|
|
|
} else {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* propp = nullptr;
|
2020-03-31 00:13:51 +02:00
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
2022-11-13 21:33:11 +01:00
|
|
|
for (AstNodeExpr* icondp = itemp->condsp(); icondp;
|
|
|
|
|
icondp = VN_AS(icondp->nextp(), NodeExpr)) {
|
|
|
|
|
AstNodeExpr* onep;
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
|
2023-09-17 04:50:54 +02:00
|
|
|
onep = rcondp->newAndFromInside(
|
2025-10-17 15:08:57 +02:00
|
|
|
nodep->exprp()->cloneTreePure(true),
|
|
|
|
|
rcondp->lhsp()->cloneTreePure(true),
|
2023-09-17 04:50:54 +02:00
|
|
|
rcondp->rhsp()->cloneTreePure(true));
|
2020-03-31 00:13:51 +02:00
|
|
|
} else if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
onep = AstEqWild::newTyped(itemp->fileline(),
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->exprp()->cloneTreePure(false),
|
|
|
|
|
icondp->cloneTreePure(false));
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
onep = AstEq::newTyped(icondp->fileline(),
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->exprp()->cloneTreePure(false),
|
|
|
|
|
icondp->cloneTreePure(false));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
if (propp) {
|
2022-10-22 01:03:40 +02:00
|
|
|
propp = new AstConcat{icondp->fileline(), onep, propp};
|
2020-04-14 04:51:35 +02:00
|
|
|
} else {
|
|
|
|
|
propp = onep;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Empty case means no property
|
2022-10-22 01:03:40 +02:00
|
|
|
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool allow_none = has_default || nodep->unique0Pragma();
|
2024-02-13 13:53:32 +01:00
|
|
|
// The following assertion lools as below.
|
|
|
|
|
// if (!$onehot(propp)) begin
|
|
|
|
|
// if (propp == '0) begin if (!allow_none) $error("none match"); end
|
|
|
|
|
// else $error("multiple match");
|
|
|
|
|
// end
|
|
|
|
|
AstNodeExpr* const ohot = new AstOneHot{nodep->fileline(), propp};
|
2024-08-06 07:45:57 +02:00
|
|
|
AstConst* const zero = new AstConst{
|
|
|
|
|
nodep->fileline(), AstConst::WidthedValue{}, propp->width(), 0};
|
2024-02-13 13:53:32 +01:00
|
|
|
AstIf* const ohotIfp
|
|
|
|
|
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}};
|
2024-08-06 07:45:57 +02:00
|
|
|
AstIf* const zeroIfp = new AstIf{
|
|
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstEq{nodep->fileline(), propp->cloneTreePure(false), zero}};
|
2024-02-13 13:53:32 +01:00
|
|
|
AstNodeExpr* const exprp = nodep->exprp();
|
|
|
|
|
const string pragmaStr = nodep->pragmaString();
|
|
|
|
|
if (!allow_none)
|
2024-07-10 11:06:13 +02:00
|
|
|
zeroIfp->addThensp(
|
2024-08-05 23:54:13 +02:00
|
|
|
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
2024-07-10 11:06:13 +02:00
|
|
|
pragmaStr + ", but none matched" + valFmt,
|
|
|
|
|
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
|
2024-02-22 09:09:14 +01:00
|
|
|
zeroIfp->addElsesp(
|
2024-08-05 23:54:13 +02:00
|
|
|
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
2024-02-23 15:05:53 +01:00
|
|
|
pragmaStr + ", but multiple matches found" + valFmt,
|
2024-02-22 09:09:14 +01:00
|
|
|
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
|
2024-02-13 13:53:32 +01:00
|
|
|
ohotIfp->addThensp(zeroIfp);
|
|
|
|
|
ohotIfp->isBoundsCheck(true); // To avoid LATCH warning
|
|
|
|
|
ohotIfp->branchPred(VBranchPred::BP_UNLIKELY);
|
|
|
|
|
nodep->addNotParallelp(ohotIfp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstFuture* nodep) override {
|
|
|
|
|
nodep->v3error("Future sampled value function called outside property or sequence "
|
|
|
|
|
"expression (IEEE 16.9.4)");
|
|
|
|
|
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-23 21:09:47 +02:00
|
|
|
//========== Past
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPast* nodep) override {
|
2018-09-23 21:09:47 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
uint32_t ticks = 1;
|
|
|
|
|
if (nodep->ticksp()) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
|
|
|
|
|
"Expected constant ticks, checked in V3Width");
|
2021-10-22 14:56:48 +02:00
|
|
|
ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
|
2018-09-23 21:09:47 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* inp = newSampledExpr(exprp);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVar* invarp = nullptr;
|
2023-01-28 18:22:23 +01:00
|
|
|
AstSenTree* const sentreep = nodep->sentreep()->unlinkFrBack();
|
2021-11-13 19:50:44 +01:00
|
|
|
AstAlways* const alwaysp
|
2022-10-22 01:03:40 +02:00
|
|
|
= new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(alwaysp);
|
2020-04-14 04:51:35 +02:00
|
|
|
for (uint32_t i = 0; i < ticks; ++i) {
|
2025-08-24 03:10:46 +02:00
|
|
|
// TODO recognize AstVarRef is getting delayed and share variables between
|
|
|
|
|
// $pasts with same reference (or same expression). Saves downstream
|
|
|
|
|
// optimizations from identifying and removing duplication.
|
2022-10-22 01:03:40 +02:00
|
|
|
AstVar* const outvarp = new AstVar{
|
2022-01-02 19:56:40 +01:00
|
|
|
nodep->fileline(), VVarType::MODULETEMP,
|
2022-10-22 01:03:40 +02:00
|
|
|
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep()};
|
2025-10-10 16:16:15 +02:00
|
|
|
outvarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2025-08-24 03:10:46 +02:00
|
|
|
++m_statPastVars;
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(outvarp);
|
2022-10-22 01:03:40 +02:00
|
|
|
AstNode* const assp = new AstAssignDly{
|
|
|
|
|
nodep->fileline(), new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}, inp};
|
2022-09-15 20:43:56 +02:00
|
|
|
alwaysp->addStmtsp(assp);
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, assp, "", "ass");
|
2018-09-23 21:09:47 +02:00
|
|
|
invarp = outvarp;
|
2022-10-22 01:03:40 +02:00
|
|
|
inp = new AstVarRef{nodep->fileline(), invarp, VAccess::READ};
|
2018-09-23 21:09:47 +02:00
|
|
|
}
|
|
|
|
|
nodep->replaceWith(inp);
|
2022-12-29 19:59:24 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2018-09-23 21:09:47 +02:00
|
|
|
}
|
2022-08-29 14:39:41 +02:00
|
|
|
|
|
|
|
|
//========== Move $sampled down to read-only variables
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSampled* nodep) override {
|
2022-08-29 14:39:41 +02:00
|
|
|
if (nodep->user1()) return;
|
|
|
|
|
VL_RESTORER(m_inSampled);
|
|
|
|
|
{
|
|
|
|
|
m_inSampled = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2020-01-26 19:38:15 +01:00
|
|
|
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2022-08-29 14:39:41 +02:00
|
|
|
iterateChildren(nodep);
|
2025-11-06 14:42:27 +01:00
|
|
|
if (m_inSampled && !VString::startsWith(nodep->name(), "__VcycleDly")) {
|
2022-08-29 14:39:41 +02:00
|
|
|
if (!nodep->access().isReadOnly()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Write to variable in sampled expression");
|
|
|
|
|
} else {
|
|
|
|
|
VNRelinker relinkHandle;
|
|
|
|
|
nodep->unlinkFrBack(&relinkHandle);
|
|
|
|
|
AstSampled* const newp = newSampledExpr(nodep);
|
|
|
|
|
relinkHandle.relink(newp);
|
|
|
|
|
newp->user1(1);
|
2024-07-10 00:31:58 +02:00
|
|
|
v3Global.setHasSampled();
|
2022-08-29 14:39:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Don't sample sensitivities
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstSenItem* nodep) override {
|
2022-08-29 14:39:41 +02:00
|
|
|
VL_RESTORER(m_inSampled);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_inSampled = false;
|
|
|
|
|
iterateChildren(nodep);
|
2022-08-29 14:39:41 +02:00
|
|
|
}
|
2025-11-06 14:42:27 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-10 16:16:15 +02:00
|
|
|
void visit(AstPExpr* nodep) override {
|
2025-11-06 14:42:27 +01:00
|
|
|
if (m_underAssert) {
|
2025-10-10 16:16:15 +02:00
|
|
|
VL_RESTORER(m_inSampled);
|
|
|
|
|
m_inSampled = false;
|
2025-11-06 14:42:27 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
} else if (m_inRestrict) {
|
2025-10-10 16:16:15 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-23 21:09:47 +02:00
|
|
|
|
|
|
|
|
//========== Statements
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDisplay* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace the special types with standard text
|
2022-01-02 19:56:40 +01:00
|
|
|
if (nodep->displayType() == VDisplayType::DT_INFO) {
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceDisplay(nodep, "-Info");
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->displayType() == VDisplayType::DT_WARNING) {
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceDisplay(nodep, "%%Warning");
|
2023-03-25 00:22:48 +01:00
|
|
|
} else if (nodep->displayType() == VDisplayType::DT_ERROR) {
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceDisplay(nodep, "%%Error");
|
2023-03-25 00:22:48 +01:00
|
|
|
} else if (nodep->displayType() == VDisplayType::DT_FATAL) {
|
|
|
|
|
replaceDisplay(nodep, "%%Fatal");
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
|
|
|
|
|
nodep->displayType(VDisplayType::DT_DISPLAY);
|
2025-03-02 23:01:35 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2024-07-24 13:18:57 +02:00
|
|
|
AstNode* monExprsp = nodep->fmtp()->exprsp();
|
|
|
|
|
AstSenItem* monSenItemsp = nullptr;
|
|
|
|
|
while (monExprsp) {
|
|
|
|
|
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
|
|
|
|
|
AstSenItem* const senItemp
|
2025-02-21 23:18:49 +01:00
|
|
|
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
|
|
|
|
// Clone so get VarRef or VarXRef as needed
|
|
|
|
|
varrefp->cloneTree(false)};
|
2024-07-24 13:18:57 +02:00
|
|
|
if (!monSenItemsp) {
|
|
|
|
|
monSenItemsp = senItemp;
|
|
|
|
|
} else {
|
|
|
|
|
monSenItemsp->addNext(senItemp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
monExprsp = monExprsp->nextp();
|
|
|
|
|
}
|
|
|
|
|
AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
|
2020-11-29 17:31:38 +01:00
|
|
|
const auto monNum = ++m_monitorNum;
|
|
|
|
|
// Where $monitor was we do "__VmonitorNum = N;"
|
2025-03-02 23:01:35 +01:00
|
|
|
AstAssign* const newsetp = new AstAssign{
|
|
|
|
|
fl, newMonitorNumVarRefp(nodep, VAccess::WRITE), new AstConst{fl, monNum}};
|
2020-11-29 17:31:38 +01:00
|
|
|
nodep->replaceWith(newsetp);
|
|
|
|
|
// Add "always_comb if (__VmonitorOn && __VmonitorNum==N) $display(...);"
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const stmtsp = nodep;
|
|
|
|
|
AstIf* const ifp = new AstIf{
|
2020-11-29 17:31:38 +01:00
|
|
|
fl,
|
|
|
|
|
new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
|
|
|
|
|
new AstEq{fl, new AstConst{fl, monNum},
|
|
|
|
|
newMonitorNumVarRefp(nodep, VAccess::READ)}},
|
2022-04-23 15:16:19 +02:00
|
|
|
stmtsp};
|
2022-10-22 01:10:06 +02:00
|
|
|
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
2020-11-29 17:31:38 +01:00
|
|
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2024-07-24 13:18:57 +02:00
|
|
|
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, monSenTree, ifp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newp);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
|
|
|
|
|
nodep->displayType(VDisplayType::DT_DISPLAY);
|
2020-11-29 17:31:38 +01:00
|
|
|
// Need one-shot
|
2025-03-02 23:01:35 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstVar* const varp
|
2022-01-02 19:56:40 +01:00
|
|
|
= new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
|
2020-11-29 17:31:38 +01:00
|
|
|
nodep->findBitDType()};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(varp);
|
2020-11-29 17:31:38 +01:00
|
|
|
// Where $strobe was we do "__Vstrobe = '1;"
|
2025-03-02 23:01:35 +01:00
|
|
|
AstAssign* const newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
|
|
|
|
new AstConst{fl, AstConst::BitTrue{}}};
|
2020-11-29 17:31:38 +01:00
|
|
|
nodep->replaceWith(newsetp);
|
|
|
|
|
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const stmtsp = nodep;
|
2022-04-23 15:16:19 +02:00
|
|
|
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
|
2022-10-22 01:10:06 +02:00
|
|
|
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
2020-11-29 17:31:38 +01:00
|
|
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
2020-11-29 17:31:38 +01:00
|
|
|
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
|
|
|
|
new AstConst{fl, AstConst::BitFalse{}}});
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMonitorOff* nodep) override {
|
2025-03-02 23:01:35 +01:00
|
|
|
AstAssign* const newp
|
2022-10-22 01:03:40 +02:00
|
|
|
= new AstAssign{nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
|
|
|
|
|
new AstConst{nodep->fileline(), AstConst::BitTrue{}, nodep->off()}};
|
2020-11-29 17:31:38 +01:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstAssert* nodep) override { //
|
|
|
|
|
visitAssertionIterate(nodep, nodep->failsp());
|
2024-05-08 14:31:34 +02:00
|
|
|
}
|
|
|
|
|
void visit(AstAssertCtl* nodep) override {
|
|
|
|
|
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
2024-07-10 11:06:13 +02:00
|
|
|
if (!resolveAssertType(nodep)) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: non-constant assert assertion-type expression");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
2024-08-05 23:54:13 +02:00
|
|
|
}
|
|
|
|
|
if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
|
|
|
|
&& nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
|
|
|
|
|
| VAssertType::UNIQUE0
|
|
|
|
|
| VAssertType::PRIORITY)) {
|
2024-07-10 11:06:13 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!resolveControlType(nodep)) {
|
2024-05-08 14:31:34 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-08-05 23:54:13 +02:00
|
|
|
if (!resolveDirectiveType(nodep)) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: non-const assert directive type expression");
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-05-08 14:31:34 +02:00
|
|
|
|
2024-07-10 11:06:13 +02:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2024-05-08 14:31:34 +02:00
|
|
|
switch (nodep->ctlType()) {
|
|
|
|
|
case VAssertCtlType::ON:
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
2024-08-05 23:54:13 +02:00
|
|
|
nodep->replaceWith(new AstCExpr{
|
|
|
|
|
fl,
|
|
|
|
|
"vlSymsp->_vm_contextp__->assertOnSet("s + std::to_string(nodep->ctlAssertTypes())
|
|
|
|
|
+ ", "s + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s,
|
|
|
|
|
1});
|
2024-07-10 11:06:13 +02:00
|
|
|
break;
|
2024-05-08 14:31:34 +02:00
|
|
|
case VAssertCtlType::OFF:
|
|
|
|
|
case VAssertCtlType::KILL: {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
2024-08-05 23:54:13 +02:00
|
|
|
nodep->replaceWith(new AstCExpr{fl,
|
|
|
|
|
"vlSymsp->_vm_contextp__->assertOnClear("s
|
|
|
|
|
+ std::to_string(nodep->ctlAssertTypes()) + " ,"s
|
|
|
|
|
+ std::to_string(nodep->ctlDirectiveTypes())
|
|
|
|
|
+ ");\n"s,
|
|
|
|
|
1});
|
2024-05-08 14:31:34 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VAssertCtlType::LOCK:
|
|
|
|
|
case VAssertCtlType::UNLOCK:
|
|
|
|
|
case VAssertCtlType::PASS_ON:
|
|
|
|
|
case VAssertCtlType::PASS_OFF:
|
|
|
|
|
case VAssertCtlType::FAIL_ON:
|
|
|
|
|
case VAssertCtlType::FAIL_OFF:
|
|
|
|
|
case VAssertCtlType::NONVACUOUS_ON:
|
|
|
|
|
case VAssertCtlType::VACUOUS_OFF: {
|
|
|
|
|
nodep->unlinkFrBack();
|
2025-10-14 02:30:47 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $assertcontrol control_type '" << cvtToStr(
|
|
|
|
|
static_cast<int>(nodep->ctlType())) << "'");
|
2024-05-08 14:31:34 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
nodep->unlinkFrBack();
|
2025-10-14 02:30:47 +02:00
|
|
|
nodep->v3warn(EC_ERROR, "Bad $assertcontrol control_type '"
|
|
|
|
|
<< cvtToStr(static_cast<int>(nodep->ctlType()))
|
|
|
|
|
<< "' (IEEE 1800-2023 Table 20-5)");
|
2024-05-08 14:31:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-12-17 03:43:52 +01:00
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstAssertIntrinsic* nodep) override { //
|
|
|
|
|
visitAssertionIterate(nodep, nodep->failsp());
|
2020-12-06 04:58:36 +01:00
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstCover* nodep) override { //
|
|
|
|
|
visitAssertionIterate(nodep, nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRestrict* nodep) override {
|
2025-10-10 16:16:15 +02:00
|
|
|
VL_RESTORER(m_inRestrict);
|
|
|
|
|
m_inRestrict = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-12-17 03:43:52 +01:00
|
|
|
// IEEE says simulator ignores these
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_modPastNum);
|
2020-11-29 17:31:38 +01:00
|
|
|
VL_RESTORER(m_modStrobeNum);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
m_modPastNum = 0;
|
|
|
|
|
m_modStrobeNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2023-02-28 06:35:37 +01:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
|
|
|
|
VL_RESTORER(m_procedurep);
|
|
|
|
|
m_procedurep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2025-09-23 20:49:01 +02:00
|
|
|
void visit(AstGenBlock* nodep) override {
|
|
|
|
|
// This code is needed rather than a visitor in V3Begin,
|
|
|
|
|
// because V3Assert is called before V3Begin
|
|
|
|
|
VL_RESTORER(m_beginp);
|
|
|
|
|
m_beginp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBegin* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// This code is needed rather than a visitor in V3Begin,
|
|
|
|
|
// because V3Assert is called before V3Begin
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_beginp);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_beginp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2008-08-06 18:52:39 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~AssertVisitor() override {
|
2019-12-17 03:43:52 +01:00
|
|
|
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
|
|
|
|
|
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
|
|
|
|
|
V3Stats::addStat("Assertions, cover statements", m_statCover);
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
|
2025-08-24 03:10:46 +02:00
|
|
|
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Top Assert class
|
|
|
|
|
|
|
|
|
|
void V3Assert::assertAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ AssertVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|