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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// Pre steps:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Attach clocks to each assertion
|
2023-10-20 01:26:36 +02:00
|
|
|
// Substitute property references by property body (IEEE 1800-2012 16.12.1).
|
2022-12-23 13:34:49 +01:00
|
|
|
// Transform clocking blocks into imperative logic
|
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 "V3AssertPre.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2022-12-23 13:34:49 +01:00
|
|
|
#include "V3Const.h"
|
2022-11-01 23:53:47 +01:00
|
|
|
#include "V3Task.h"
|
2022-12-23 13:34:49 +01:00
|
|
|
#include "V3UniqueNames.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Assert class functions
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class AssertPreVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Removes clocks and other pre-optimizations
|
|
|
|
|
// Eventually inlines calls to sequences, properties, etc.
|
|
|
|
|
// We're not parsing the tree, or anything more complicated.
|
|
|
|
|
private:
|
2022-12-23 13:34:49 +01:00
|
|
|
// NODE STATE
|
2025-09-16 19:25:40 +02:00
|
|
|
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
|
2022-12-23 13:34:49 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2022-12-23 13:34:49 +01:00
|
|
|
// Current context:
|
|
|
|
|
AstNetlist* const m_netlistp = nullptr; // Current netlist
|
|
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstClocking* m_clockingp = nullptr; // Current clocking block
|
2006-08-26 13:35:28 +02:00
|
|
|
// Reset each module:
|
2024-11-27 04:27:32 +01:00
|
|
|
AstClocking* m_defaultClockingp = nullptr; // Default clocking for current module
|
|
|
|
|
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable for current module
|
2006-08-26 13:35:28 +02:00
|
|
|
// Reset each assertion:
|
2020-08-16 15:55:36 +02:00
|
|
|
AstSenItem* m_senip = nullptr; // Last sensitivity
|
2018-09-23 21:09:47 +02:00
|
|
|
// Reset each always:
|
2020-08-16 15:55:36 +02:00
|
|
|
AstSenItem* m_seniAlwaysp = nullptr; // Last sensitivity in always
|
2020-09-10 12:49:04 +02:00
|
|
|
// Reset each assertion:
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* m_disablep = nullptr; // Last disable
|
2022-12-23 13:34:49 +01:00
|
|
|
// Other:
|
|
|
|
|
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
2025-10-10 16:16:15 +02:00
|
|
|
V3UniqueNames m_propPrecondNames{"__VpropPrecond"}; // Cycle delay temporaries name generator
|
2022-12-23 13:34:49 +01:00
|
|
|
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
|
2024-06-30 21:19:02 +02:00
|
|
|
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
2025-10-10 16:16:15 +02:00
|
|
|
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
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2023-01-28 20:05:26 +01:00
|
|
|
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Create sentree based on clocked or default clock
|
2020-08-15 16:12:55 +02:00
|
|
|
// Return nullptr for always
|
2023-01-28 20:05:26 +01:00
|
|
|
if (useTreep) return useTreep;
|
2020-08-15 16:12:55 +02:00
|
|
|
AstSenTree* newp = nullptr;
|
2020-07-05 19:13:03 +02:00
|
|
|
AstSenItem* senip = m_senip;
|
2022-12-23 13:34:49 +01:00
|
|
|
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
|
2018-09-23 21:09:47 +02:00
|
|
|
if (!senip) senip = m_seniAlwaysp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!senip) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
2022-11-20 21:06:49 +01:00
|
|
|
newp = new AstSenTree{nodep->fileline(), nullptr};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2022-11-20 21:06:49 +01:00
|
|
|
newp = new AstSenTree{nodep->fileline(), senip->cloneTree(true)};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return newp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-09-10 12:49:04 +02:00
|
|
|
void clearAssertInfo() {
|
|
|
|
|
m_senip = nullptr;
|
|
|
|
|
m_disablep = nullptr;
|
|
|
|
|
}
|
2022-11-01 23:53:47 +01:00
|
|
|
AstPropSpec* getPropertyExprp(const AstProperty* const propp) {
|
|
|
|
|
// The only statements possible in AstProperty are AstPropSpec (body)
|
|
|
|
|
// and AstVar (arguments).
|
|
|
|
|
AstNode* propExprp = propp->stmtsp();
|
|
|
|
|
while (VN_IS(propExprp, Var)) propExprp = propExprp->nextp();
|
|
|
|
|
return VN_CAST(propExprp, PropSpec);
|
|
|
|
|
}
|
|
|
|
|
AstPropSpec* substitutePropertyCall(AstPropSpec* nodep) {
|
|
|
|
|
if (AstFuncRef* const funcrefp = VN_CAST(nodep->propp(), FuncRef)) {
|
2025-08-19 23:02:10 +02:00
|
|
|
if (const AstProperty* const propp = VN_CAST(funcrefp->taskp(), Property)) {
|
2022-11-01 23:53:47 +01:00
|
|
|
AstPropSpec* propExprp = getPropertyExprp(propp);
|
2022-12-03 00:46:38 +01:00
|
|
|
// Substitute inner property call before copying in order to not doing the same for
|
2022-11-01 23:53:47 +01:00
|
|
|
// each call of outer property call.
|
|
|
|
|
propExprp = substitutePropertyCall(propExprp);
|
|
|
|
|
// Clone subtree after substitution. It is needed, because property might be called
|
|
|
|
|
// multiple times with different arguments.
|
|
|
|
|
propExprp = propExprp->cloneTree(false);
|
|
|
|
|
// Substitute formal arguments with actual arguments
|
|
|
|
|
const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, propp->stmtsp());
|
|
|
|
|
for (const auto& tconnect : tconnects) {
|
|
|
|
|
const AstVar* const portp = tconnect.first;
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariablePointer // 'exprp' unlinked below
|
2022-11-01 23:53:47 +01:00
|
|
|
AstArg* const argp = tconnect.second;
|
2025-09-09 23:39:44 +02:00
|
|
|
propExprp->foreach([&](AstVarRef* refp) {
|
|
|
|
|
if (refp->varp() == portp) {
|
|
|
|
|
refp->replaceWith(argp->exprp()->cloneTree(false));
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(refp), refp);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
pushDeletep(argp->exprp()->unlinkFrBack());
|
2022-11-01 23:53:47 +01:00
|
|
|
}
|
2024-03-02 15:05:21 +01:00
|
|
|
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
|
2022-11-01 23:53:47 +01:00
|
|
|
if (nodep->disablep() && propExprp->disablep()) {
|
|
|
|
|
nodep->v3error("disable iff expression before property call and in its "
|
|
|
|
|
"body is not legal");
|
2022-11-11 04:09:24 +01:00
|
|
|
pushDeletep(propExprp->disablep()->unlinkFrBack());
|
2022-11-01 23:53:47 +01:00
|
|
|
}
|
|
|
|
|
// If disable iff is in outer property, move it to inner
|
|
|
|
|
if (nodep->disablep()) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const disablep = nodep->disablep()->unlinkFrBack();
|
2022-11-01 23:53:47 +01:00
|
|
|
propExprp->disablep(disablep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodep->sensesp() && propExprp->sensesp()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Clock event before property call and in its body");
|
|
|
|
|
pushDeletep(propExprp->sensesp()->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
// If clock event is in outer property, move it to inner
|
|
|
|
|
if (nodep->sensesp()) {
|
|
|
|
|
AstSenItem* const sensesp = nodep->sensesp();
|
|
|
|
|
sensesp->unlinkFrBack();
|
|
|
|
|
propExprp->sensesp(sensesp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now substitute property reference with property body
|
|
|
|
|
nodep->replaceWith(propExprp);
|
2025-09-09 23:39:44 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-11-01 23:53:47 +01:00
|
|
|
return propExprp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nodep;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-09-23 21:09:47 +02:00
|
|
|
// VISITORS
|
|
|
|
|
//========== Statements
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstClocking* const nodep) override {
|
|
|
|
|
VL_RESTORER(m_clockingp);
|
|
|
|
|
m_clockingp = nodep;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " CLOCKING" << nodep);
|
2022-12-23 13:34:49 +01:00
|
|
|
iterateChildren(nodep);
|
2024-06-30 21:19:02 +02:00
|
|
|
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
2025-09-16 19:25:40 +02:00
|
|
|
void visit(AstModportClockingRef* const nodep) override {
|
|
|
|
|
// It has to be converted to a list of ModportClockingVarRefs,
|
|
|
|
|
// because clocking blocks are removed in this pass
|
|
|
|
|
for (AstClockingItem* itemp = nodep->clockingp()->itemsp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), ClockingItem)) {
|
|
|
|
|
AstVar* const varp = itemp->varp() ? itemp->varp() : VN_AS(itemp->user1p(), Var);
|
|
|
|
|
if (varp) {
|
|
|
|
|
AstModportVarRef* const modVarp
|
|
|
|
|
= new AstModportVarRef{nodep->fileline(), varp->name(), itemp->direction()};
|
|
|
|
|
modVarp->varp(varp);
|
|
|
|
|
nodep->addNextHere(modVarp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstClockingItem* const nodep) override {
|
2024-06-07 14:30:58 +02:00
|
|
|
// Get a ref to the sampled/driven variable
|
|
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
if (!varp) {
|
|
|
|
|
// Unused item
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
V3Const::constifyEdit(nodep->skewp());
|
|
|
|
|
if (!VN_IS(nodep->skewp(), Const)) {
|
2024-03-02 15:05:21 +01:00
|
|
|
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2023 14.4)");
|
2022-12-23 13:34:49 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
|
|
|
|
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
|
|
|
|
AstNodeExpr* const exprp = nodep->exprp();
|
2024-06-30 21:19:02 +02:00
|
|
|
varp->name(m_clockingp->name() + "__DOT__" + varp->name());
|
|
|
|
|
m_clockingp->addNextHere(varp->unlinkFrBack());
|
2025-09-16 19:25:40 +02:00
|
|
|
nodep->user1p(varp);
|
2022-12-23 13:34:49 +01:00
|
|
|
varp->user1p(nodep);
|
|
|
|
|
if (nodep->direction() == VDirection::OUTPUT) {
|
2025-08-19 23:02:10 +02:00
|
|
|
exprp->foreach([](const AstNodeVarRef* varrefp) {
|
2024-08-08 22:48:25 +02:00
|
|
|
// Prevent confusing BLKANDNBLK warnings on clockvars due to generated assignments
|
|
|
|
|
varrefp->fileline()->warnOff(V3ErrorCode::BLKANDNBLK, true);
|
|
|
|
|
});
|
|
|
|
|
AstVarRef* const skewedReadRefp = new AstVarRef{flp, varp, VAccess::READ};
|
|
|
|
|
skewedReadRefp->user1(true);
|
|
|
|
|
// Initialize the clockvar
|
|
|
|
|
AstVarRef* const skewedWriteRefp = new AstVarRef{flp, varp, VAccess::WRITE};
|
|
|
|
|
skewedWriteRefp->user1(true);
|
|
|
|
|
AstInitialStatic* const initClockvarp = new AstInitialStatic{
|
|
|
|
|
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
|
|
|
|
|
m_modp->addStmtsp(initClockvarp);
|
|
|
|
|
// A var to keep the previous value of the clockvar
|
|
|
|
|
AstVar* const prevVarp = new AstVar{
|
|
|
|
|
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
|
2025-09-22 01:52:19 +02:00
|
|
|
prevVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
2024-08-08 22:48:25 +02:00
|
|
|
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
|
|
|
|
|
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
|
|
|
|
skewedReadRefp->cloneTreePure(false)}};
|
|
|
|
|
m_modp->addStmtsp(prevVarp);
|
|
|
|
|
m_modp->addStmtsp(initPrevClockvarp);
|
|
|
|
|
// Assign the clockvar to the actual var; only do it if the clockvar's value has
|
|
|
|
|
// changed
|
|
|
|
|
AstAssign* const assignp
|
|
|
|
|
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
|
|
|
|
|
AstIf* const ifp
|
|
|
|
|
= new AstIf{flp,
|
|
|
|
|
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
|
|
|
|
|
skewedReadRefp->cloneTreePure(false)},
|
|
|
|
|
assignp};
|
|
|
|
|
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
|
|
|
|
skewedReadRefp->cloneTree(false)});
|
2022-12-23 13:34:49 +01:00
|
|
|
if (skewp->isZero()) {
|
2024-03-02 15:05:21 +01:00
|
|
|
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
|
2024-08-30 09:14:41 +02:00
|
|
|
AstSenTree* senTreep
|
|
|
|
|
= new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)};
|
|
|
|
|
senTreep->addSensesp(
|
|
|
|
|
new AstSenItem{flp, VEdgeType::ET_CHANGED, skewedReadRefp->cloneTree(false)});
|
|
|
|
|
AstCMethodHard* const trigp = new AstCMethodHard{
|
|
|
|
|
nodep->fileline(),
|
2025-09-27 14:22:17 +02:00
|
|
|
new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ},
|
|
|
|
|
VCMethod::EVENT_IS_TRIGGERED};
|
2024-08-30 09:14:41 +02:00
|
|
|
trigp->dtypeSetBit();
|
|
|
|
|
ifp->condp(new AstLogAnd{flp, ifp->condp()->unlinkFrBack(), trigp});
|
|
|
|
|
m_clockingp->addNextHere(new AstAlwaysReactive{flp, senTreep, ifp});
|
2022-12-23 13:34:49 +01:00
|
|
|
} else if (skewp->fileline()->timingOn()) {
|
|
|
|
|
// Create a fork so that this AlwaysObserved can be retriggered before the
|
|
|
|
|
// assignment happens. Also then it can be combo, avoiding the need for creating
|
|
|
|
|
// new triggers.
|
2025-10-24 15:00:07 +02:00
|
|
|
AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE};
|
|
|
|
|
forkp->addForksp(new AstBegin{flp, "", ifp, true});
|
2022-12-23 13:34:49 +01:00
|
|
|
// Use Observed for this to make sure we do not miss the event
|
|
|
|
|
m_clockingp->addNextHere(new AstAlwaysObserved{
|
|
|
|
|
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, forkp});
|
|
|
|
|
if (v3Global.opt.timing().isSetTrue()) {
|
2024-01-06 00:07:24 +01:00
|
|
|
AstDelay* const delayp = new AstDelay{flp, skewp->unlinkFrBack(), false};
|
|
|
|
|
delayp->timeunit(m_modp->timeunit());
|
|
|
|
|
assignp->timingControlp(delayp);
|
2022-12-23 13:34:49 +01:00
|
|
|
} else if (v3Global.opt.timing().isSetFalse()) {
|
|
|
|
|
nodep->v3warn(E_NOTIMING,
|
|
|
|
|
"Clocking output skew greater than #0 requires --timing");
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(E_NEEDTIMINGOPT,
|
|
|
|
|
"Use --timing or --no-timing to specify how "
|
|
|
|
|
"clocking output skew greater than #0 should be handled");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (nodep->direction() == VDirection::INPUT) {
|
|
|
|
|
// Ref to the clockvar
|
|
|
|
|
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
|
|
|
|
|
refp->user1(true);
|
|
|
|
|
if (skewp->num().is1Step()) {
|
2024-03-02 15:05:21 +01:00
|
|
|
// Assign the sampled expression to the clockvar (IEEE 1800-2023 14.13)
|
2023-09-17 04:50:54 +02:00
|
|
|
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTreePure(false)};
|
2022-12-23 13:34:49 +01:00
|
|
|
sampledp->dtypeFrom(exprp);
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const ap = new AstAssignW{flp, refp, sampledp};
|
|
|
|
|
m_clockingp->addNextHere(new AstAlways{ap});
|
2022-12-23 13:34:49 +01:00
|
|
|
} else if (skewp->isZero()) {
|
2024-03-02 15:05:21 +01:00
|
|
|
// #0 means the var has to be sampled in Observed (IEEE 1800-2023 14.13)
|
2023-09-17 04:50:54 +02:00
|
|
|
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTreePure(false)};
|
2022-12-23 13:34:49 +01:00
|
|
|
m_clockingp->addNextHere(new AstAlwaysObserved{
|
|
|
|
|
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
|
|
|
|
} else {
|
|
|
|
|
// Create a queue where we'll store sampled values with timestamps
|
|
|
|
|
AstSampleQueueDType* const queueDtp
|
|
|
|
|
= new AstSampleQueueDType{flp, exprp->dtypep()};
|
|
|
|
|
m_netlistp->typeTablep()->addTypesp(queueDtp);
|
2024-06-30 21:19:02 +02:00
|
|
|
AstVar* const queueVarp
|
|
|
|
|
= new AstVar{flp, VVarType::MODULETEMP, "__Vqueue__" + varp->name(), queueDtp};
|
2025-09-22 01:52:19 +02:00
|
|
|
queueVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
2022-12-23 13:34:49 +01:00
|
|
|
m_clockingp->addNextHere(queueVarp);
|
|
|
|
|
// Create a process like this:
|
|
|
|
|
// always queue.push(<sampled var>);
|
|
|
|
|
AstCMethodHard* const pushp = new AstCMethodHard{
|
2025-09-27 14:22:17 +02:00
|
|
|
flp, new AstVarRef{flp, queueVarp, VAccess::WRITE}, VCMethod::DYN_PUSH,
|
2023-04-07 03:00:10 +02:00
|
|
|
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
2023-09-17 04:50:54 +02:00
|
|
|
pushp->addPinsp(exprp->cloneTreePure(false));
|
2022-12-23 13:34:49 +01:00
|
|
|
pushp->dtypeSetVoid();
|
|
|
|
|
m_clockingp->addNextHere(
|
|
|
|
|
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, pushp->makeStmt()});
|
|
|
|
|
// Create a process like this:
|
2024-06-30 21:19:02 +02:00
|
|
|
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>);
|
2022-12-23 13:34:49 +01:00
|
|
|
AstCMethodHard* const popp = new AstCMethodHard{
|
2025-09-27 14:22:17 +02:00
|
|
|
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
|
2023-04-07 03:00:10 +02:00
|
|
|
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
2022-12-23 13:34:49 +01:00
|
|
|
popp->addPinsp(skewp->unlinkFrBack());
|
|
|
|
|
popp->addPinsp(refp);
|
|
|
|
|
popp->dtypeSetVoid();
|
|
|
|
|
m_clockingp->addNextHere(
|
|
|
|
|
new AstAlways{flp, VAlwaysKwd::ALWAYS,
|
|
|
|
|
new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)},
|
|
|
|
|
popp->makeStmt()});
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2025-03-24 00:51:54 +01:00
|
|
|
nodep->v3fatalSrc("Invalid direction");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstDelay* nodep) override {
|
|
|
|
|
// Only cycle delays are relevant in this stage; also only process once
|
|
|
|
|
if (!nodep->isCycleDelay()) {
|
|
|
|
|
if (m_inSynchDrive) {
|
|
|
|
|
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
2024-03-02 15:05:21 +01:00
|
|
|
" (IEEE 1800-2023 14.16)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
2024-06-30 21:19:02 +02:00
|
|
|
iterateChildren(nodep);
|
2022-12-23 13:34:49 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (m_inAssign && !m_inSynchDrive) {
|
|
|
|
|
nodep->v3error("Cycle delays not allowed as intra-assignment delays"
|
2024-03-02 15:05:21 +01:00
|
|
|
" (IEEE 1800-2023 14.11)");
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstConst* const constp = VN_CAST(valuep, Const);
|
2022-12-23 13:34:49 +01:00
|
|
|
if (constp->isZero()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2024-03-23 23:12:43 +01:00
|
|
|
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
2022-12-23 13:34:49 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2025-10-10 16:16:15 +02:00
|
|
|
AstSenItem* sensesp = nullptr;
|
2022-12-23 13:34:49 +01:00
|
|
|
if (!m_defaultClockingp) {
|
2025-10-10 16:16:15 +02:00
|
|
|
if (!m_pExpr && !m_inSExpr) {
|
|
|
|
|
nodep->v3error("Usage of cycle delays requires default clocking"
|
|
|
|
|
" (IEEE 1800-2023 14.11)");
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sensesp = m_senip;
|
|
|
|
|
} else {
|
|
|
|
|
sensesp = m_defaultClockingp->sensesp();
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
AstEventControl* const controlp = new AstEventControl{
|
2025-10-10 16:16:15 +02:00
|
|
|
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
|
2022-12-23 13:34:49 +01:00
|
|
|
const std::string delayName = m_cycleDlyNames.get(nodep);
|
|
|
|
|
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
|
|
|
|
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
2025-09-22 01:52:19 +02:00
|
|
|
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2025-09-23 20:49:01 +02:00
|
|
|
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
|
2022-12-23 13:34:49 +01:00
|
|
|
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
2025-09-29 16:25:25 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
AstLoop* const loopp = new AstLoop{flp};
|
|
|
|
|
loopp->addStmtsp(new AstLoopTest{
|
|
|
|
|
flp, loopp,
|
|
|
|
|
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}}});
|
|
|
|
|
loopp->addStmtsp(controlp);
|
|
|
|
|
loopp->addStmtsp(
|
|
|
|
|
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
|
|
|
|
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
|
|
|
|
new AstConst{flp, 1}}});
|
|
|
|
|
beginp->addStmtsp(loopp);
|
|
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
nodep->replaceWith(beginp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstSenTree* nodep) override {
|
|
|
|
|
if (m_inSynchDrive) {
|
|
|
|
|
nodep->v3error("Event controls cannot be used in "
|
2024-03-02 15:05:21 +01:00
|
|
|
"synchronous drives (IEEE 1800-2023 14.16)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " -varref: " << nodep);
|
|
|
|
|
UINFO(8, " -varref-var-back: " << nodep->varp()->backp());
|
|
|
|
|
UINFO(8, " -varref-var-user1: " << nodep->varp()->user1p());
|
2024-06-30 21:19:02 +02:00
|
|
|
if (AstClockingItem* const itemp = VN_CAST(
|
|
|
|
|
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
|
|
|
|
ClockingItem)) {
|
|
|
|
|
if (nodep->user1()) return;
|
|
|
|
|
|
|
|
|
|
// ensure linking still works, this has to be done only once
|
|
|
|
|
if (AstVarXRef* xrefp = VN_CAST(nodep, VarXRef)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " -clockvarxref-in: " << xrefp);
|
2024-06-30 21:19:02 +02:00
|
|
|
string dotted = xrefp->dotted();
|
|
|
|
|
const size_t dotPos = dotted.rfind('.');
|
|
|
|
|
dotted.erase(dotPos, string::npos);
|
|
|
|
|
xrefp->dotted(dotted);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " -clockvarxref-out: " << xrefp);
|
2024-06-30 21:19:02 +02:00
|
|
|
m_xrefsp.emplace_back(xrefp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
2024-03-02 15:05:21 +01:00
|
|
|
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
if (nodep->access().isWriteOrRW()) {
|
|
|
|
|
if (itemp->direction() == VDirection::OUTPUT) {
|
|
|
|
|
if (!m_inAssignDlyLhs) {
|
|
|
|
|
nodep->v3error("Only non-blocking assignments can write "
|
2024-03-02 15:05:21 +01:00
|
|
|
"to clockvars (IEEE 1800-2023 14.16)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
if (m_inAssign) m_inSynchDrive = true;
|
2024-06-30 21:19:02 +02:00
|
|
|
} else if (itemp->direction() == VDirection::INPUT) {
|
2024-03-02 15:05:21 +01:00
|
|
|
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-06-30 21:19:02 +02:00
|
|
|
nodep->user1(true);
|
2025-10-10 16:16:15 +02:00
|
|
|
} 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);
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-07-10 00:31:58 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
|
|
|
|
if (AstClockingItem* const itemp = VN_CAST(
|
|
|
|
|
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
|
|
|
|
ClockingItem)) {
|
|
|
|
|
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
|
|
|
|
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
|
|
|
|
}
|
|
|
|
|
if (nodep->access().isWriteOrRW()) {
|
|
|
|
|
if (itemp->direction() == VDirection::OUTPUT) {
|
|
|
|
|
if (!m_inAssignDlyLhs) {
|
|
|
|
|
nodep->v3error("Only non-blocking assignments can write "
|
|
|
|
|
"to clockvars (IEEE 1800-2023 14.16)");
|
|
|
|
|
}
|
|
|
|
|
if (m_inAssign) m_inSynchDrive = true;
|
|
|
|
|
} else if (itemp->direction() == VDirection::INPUT) {
|
|
|
|
|
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
|
|
|
|
if (nodep->user1()) return;
|
|
|
|
|
VL_RESTORER(m_inAssign);
|
|
|
|
|
VL_RESTORER(m_inSynchDrive);
|
|
|
|
|
m_inAssign = true;
|
|
|
|
|
m_inSynchDrive = false;
|
|
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_inAssignDlyLhs);
|
|
|
|
|
m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
|
|
|
|
|
iterate(nodep->lhsp());
|
|
|
|
|
}
|
|
|
|
|
iterate(nodep->rhsp());
|
|
|
|
|
if (nodep->timingControlp()) {
|
|
|
|
|
iterate(nodep->timingControlp());
|
|
|
|
|
} else if (m_inSynchDrive) {
|
|
|
|
|
AstAssign* const assignp = new AstAssign{
|
|
|
|
|
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
|
|
|
|
|
assignp->user1(true);
|
|
|
|
|
nodep->replaceWith(assignp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2008-08-06 18:52:39 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlways* nodep) override {
|
2025-08-18 01:14:34 +02:00
|
|
|
iterateAndNextNull(nodep->sentreep());
|
|
|
|
|
if (nodep->sentreep()) m_seniAlwaysp = nodep->sentreep()->sensesp();
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2020-08-15 16:12:55 +02:00
|
|
|
m_seniAlwaysp = nullptr;
|
2018-09-23 21:09:47 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCoverOrAssert* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->sentreep()) return; // Already processed
|
2025-10-10 16:16:15 +02:00
|
|
|
|
|
|
|
|
VL_RESTORER(m_hasSExpr);
|
|
|
|
|
VL_RESTORER(m_hasUnsupp);
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
clearAssertInfo();
|
2025-10-10 16:16:15 +02:00
|
|
|
m_pExpr = new AstPExpr{nodep->propp()->fileline()};
|
|
|
|
|
m_pExpr->dtypeFrom(nodep->propp());
|
|
|
|
|
|
2019-12-17 03:43:52 +01:00
|
|
|
// Find Clocking's buried under nodep->exprsp
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
|
2025-10-10 16:16:15 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
clearAssertInfo();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstFalling* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
|
|
|
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
|
|
|
|
AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)};
|
|
|
|
|
futurep->dtypeFrom(exprp);
|
|
|
|
|
exprp = new AstAnd{fl, exprp->cloneTreePure(false), new AstNot{fl, futurep}};
|
|
|
|
|
exprp->dtypeSetBit();
|
|
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFell* nodep) override {
|
2023-01-28 20:05:26 +01:00
|
|
|
if (nodep->user1SetOnce()) return;
|
2020-08-14 13:37:10 +02:00
|
|
|
iterateChildren(nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
2022-11-20 21:06:49 +01:00
|
|
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
2023-01-28 20:05:26 +01:00
|
|
|
AstSenTree* sentreep = nodep->sentreep();
|
|
|
|
|
if (sentreep) sentreep->unlinkFrBack();
|
2025-10-10 16:16:15 +02:00
|
|
|
AstPast* const pastp = new AstPast{fl, exprp};
|
2025-08-24 03:10:46 +02:00
|
|
|
pastp->dtypeFrom(exprp);
|
2025-10-10 16:16:15 +02:00
|
|
|
pastp->sentreep(newSenTree(nodep, sentreep));
|
2025-08-24 03:10:46 +02:00
|
|
|
exprp = new AstAnd{fl, pastp, new AstNot{fl, exprp->cloneTreePure(false)}};
|
2020-11-29 14:23:36 +01:00
|
|
|
exprp->dtypeSetBit();
|
2020-08-14 13:37:10 +02:00
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstFuture* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
AstSenTree* const sentreep = nodep->sentreep();
|
2025-09-09 23:39:44 +02:00
|
|
|
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
|
2025-08-24 03:16:53 +02:00
|
|
|
nodep->sentreep(newSenTree(nodep));
|
|
|
|
|
}
|
2025-10-10 16:16:15 +02:00
|
|
|
void visit(AstLogNot* nodep) override {
|
|
|
|
|
if (m_inSExpr) nodep->v3error("Syntax error: unexpected 'not' in sequence expression");
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPast* nodep) override {
|
2018-09-23 21:09:47 +02:00
|
|
|
if (nodep->sentreep()) return; // Already processed
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2018-09-23 21:09:47 +02:00
|
|
|
nodep->sentreep(newSenTree(nodep));
|
|
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstRising* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
|
|
|
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
|
|
|
|
AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)};
|
|
|
|
|
futurep->dtypeFrom(exprp);
|
|
|
|
|
exprp = new AstAnd{fl, new AstNot{fl, exprp->cloneTreePure(false)}, futurep};
|
|
|
|
|
exprp->dtypeSetBit();
|
|
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRose* nodep) override {
|
2023-01-28 20:05:26 +01:00
|
|
|
if (nodep->user1SetOnce()) return;
|
2020-08-14 13:37:10 +02:00
|
|
|
iterateChildren(nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
2022-11-20 21:06:49 +01:00
|
|
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
2023-01-28 20:05:26 +01:00
|
|
|
AstSenTree* sentreep = nodep->sentreep();
|
|
|
|
|
if (sentreep) sentreep->unlinkFrBack();
|
2025-10-10 16:16:15 +02:00
|
|
|
AstPast* const pastp = new AstPast{fl, exprp};
|
2025-08-24 03:10:46 +02:00
|
|
|
pastp->dtypeFrom(exprp);
|
2025-10-10 16:16:15 +02:00
|
|
|
pastp->sentreep(newSenTree(nodep, sentreep));
|
2025-08-24 03:10:46 +02:00
|
|
|
exprp = new AstAnd{fl, new AstNot{fl, pastp}, exprp->cloneTreePure(false)};
|
2020-11-29 14:23:36 +01:00
|
|
|
exprp->dtypeSetBit();
|
2020-08-14 13:37:10 +02:00
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstStable* nodep) override {
|
2023-01-28 20:05:26 +01:00
|
|
|
if (nodep->user1SetOnce()) return;
|
2020-07-29 00:26:24 +02:00
|
|
|
iterateChildren(nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
2023-01-28 20:05:26 +01:00
|
|
|
AstSenTree* sentreep = nodep->sentreep();
|
|
|
|
|
if (sentreep) sentreep->unlinkFrBack();
|
2025-10-10 16:16:15 +02:00
|
|
|
AstPast* const pastp = new AstPast{fl, exprp};
|
2025-08-24 03:10:46 +02:00
|
|
|
pastp->dtypeFrom(exprp);
|
2025-10-10 16:16:15 +02:00
|
|
|
pastp->sentreep(newSenTree(nodep, sentreep));
|
2025-08-24 03:10:46 +02:00
|
|
|
exprp = new AstEq{fl, pastp, exprp->cloneTreePure(false)};
|
2020-11-29 14:23:36 +01:00
|
|
|
exprp->dtypeSetBit();
|
2020-09-10 12:49:04 +02:00
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2025-08-24 03:16:53 +02:00
|
|
|
void visit(AstSteady* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
|
|
|
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
|
|
|
|
AstNodeExpr* const futurep = new AstFuture{fl, exprp, newSenTree(nodep)};
|
|
|
|
|
futurep->dtypeFrom(exprp);
|
|
|
|
|
exprp = new AstEq{fl, exprp->cloneTreePure(false), futurep};
|
|
|
|
|
exprp->dtypeSetBit();
|
|
|
|
|
nodep->replaceWith(exprp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2020-09-10 12:49:04 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstImplication* nodep) override {
|
2020-09-10 12:49:04 +02:00
|
|
|
if (nodep->sentreep()) return; // Already processed
|
2025-10-10 16:16:15 +02:00
|
|
|
m_hasUnsupp = nodep;
|
2020-09-10 12:49:04 +02:00
|
|
|
|
2025-10-10 16:16:15 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
|
2025-10-10 16:16:15 +02:00
|
|
|
if (nodep->isOverlapped()) {
|
|
|
|
|
nodep->replaceWith(new AstLogOr{flp, new AstLogNot{flp, lhsp}, rhsp});
|
|
|
|
|
} else {
|
|
|
|
|
if (m_disablep) {
|
|
|
|
|
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
|
|
|
|
}
|
2020-09-10 12:49:04 +02:00
|
|
|
|
2025-10-10 16:16:15 +02:00
|
|
|
AstPast* const pastp = new AstPast{flp, lhsp};
|
|
|
|
|
pastp->dtypeFrom(lhsp);
|
|
|
|
|
pastp->sentreep(newSenTree(nodep));
|
|
|
|
|
AstNodeExpr* const exprp = new AstOr{flp, new AstNot{flp, pastp}, rhsp};
|
|
|
|
|
exprp->dtypeSetBit();
|
|
|
|
|
nodep->replaceWith(exprp);
|
2025-09-09 23:39:44 +02:00
|
|
|
}
|
2020-07-29 00:26:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-27 04:27:32 +01:00
|
|
|
void visit(AstDefaultDisable* nodep) override {
|
|
|
|
|
// Done with these
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstInferredDisable* nodep) override {
|
|
|
|
|
AstNode* newp;
|
|
|
|
|
if (m_defaultDisablep) {
|
|
|
|
|
newp = m_defaultDisablep->condp()->cloneTreePure(true);
|
|
|
|
|
} else {
|
|
|
|
|
newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-01 23:53:47 +01:00
|
|
|
void visit(AstPropSpec* nodep) override {
|
|
|
|
|
nodep = substitutePropertyCall(nodep);
|
2018-09-23 21:09:47 +02:00
|
|
|
// No need to iterate the body, once replace will get iterated
|
|
|
|
|
iterateAndNextNull(nodep->sensesp());
|
2020-06-10 01:20:16 +02:00
|
|
|
if (m_senip)
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Block is the new expression to evaluate
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* blockp = VN_AS(nodep->propp()->unlinkFrBack(), NodeExpr);
|
2024-11-27 04:27:32 +01:00
|
|
|
if (!nodep->disablep() && m_defaultDisablep) {
|
|
|
|
|
nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
|
|
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
if (AstNodeExpr* const disablep = nodep->disablep()) {
|
2025-09-09 23:39:44 +02:00
|
|
|
m_disablep = disablep;
|
2025-10-10 16:16:15 +02:00
|
|
|
m_hasUnsupp = disablep;
|
2019-12-22 21:49:10 +01:00
|
|
|
if (VN_IS(nodep->backp(), Cover)) {
|
2022-11-20 21:06:49 +01:00
|
|
|
blockp = new AstAnd{disablep->fileline(),
|
|
|
|
|
new AstNot{disablep->fileline(), disablep->unlinkFrBack()},
|
|
|
|
|
blockp};
|
2019-12-22 21:49:10 +01:00
|
|
|
} else {
|
2022-11-20 21:06:49 +01:00
|
|
|
blockp = new AstOr{disablep->fileline(), disablep->unlinkFrBack(), blockp};
|
2019-12-22 21:49:10 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Unlink and just keep a pointer to it, convert to sentree as needed
|
|
|
|
|
m_senip = nodep->sensesp();
|
|
|
|
|
nodep->replaceWith(blockp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-10-10 16:16:15 +02:00
|
|
|
void visit(AstSExpr* nodep) override {
|
|
|
|
|
VL_RESTORER(m_inSExpr);
|
|
|
|
|
m_inSExpr = true;
|
|
|
|
|
m_hasSExpr = true;
|
|
|
|
|
|
|
|
|
|
UASSERT_OBJ(m_pExpr, nodep, "Should be under assertion");
|
|
|
|
|
m_pExpr->addPrecondp(nodep->delayp()->unlinkFrBack());
|
|
|
|
|
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_defaultClockingp);
|
2024-11-27 04:27:32 +01:00
|
|
|
VL_RESTORER(m_defaultDisablep);
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_defaultClockingp = nullptr;
|
|
|
|
|
nodep->foreach([&](AstClocking* const clockingp) {
|
|
|
|
|
if (clockingp->isDefault()) {
|
|
|
|
|
if (m_defaultClockingp) {
|
|
|
|
|
clockingp->v3error("Only one default clocking block allowed per module"
|
2024-03-02 15:05:21 +01:00
|
|
|
" (IEEE 1800-2023 14.12)");
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
m_defaultClockingp = clockingp;
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-11-27 04:27:32 +01:00
|
|
|
m_defaultDisablep = nullptr;
|
|
|
|
|
nodep->foreach([&](AstDefaultDisable* const disablep) {
|
|
|
|
|
if (m_defaultDisablep) {
|
|
|
|
|
disablep->v3error("Only one 'default disable iff' allowed per module"
|
|
|
|
|
" (IEEE 1800-2023 16.15)");
|
|
|
|
|
}
|
|
|
|
|
m_defaultDisablep = disablep;
|
|
|
|
|
});
|
2022-12-23 13:34:49 +01:00
|
|
|
m_modp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-11-01 23:53:47 +01:00
|
|
|
void visit(AstProperty* nodep) override {
|
|
|
|
|
// The body will be visited when will be substituted in place of property reference
|
|
|
|
|
// (AstFuncRef)
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-12-23 13:34:49 +01:00
|
|
|
explicit AssertPreVisitor(AstNetlist* nodep)
|
|
|
|
|
: m_netlistp{nodep} {
|
2019-05-19 22:13:13 +02:00
|
|
|
clearAssertInfo();
|
|
|
|
|
// Process
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2024-06-30 21:19:02 +02:00
|
|
|
// Fix up varref names
|
|
|
|
|
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~AssertPreVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Top Assert class
|
|
|
|
|
|
|
|
|
|
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ AssertPreVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|