Patch coverage for Verilator 5d1b4fe8a..0b11de521
Current view: top level - src/V3Assert.cpp (source / functions) Coverage Total Missed
Test: verilator-patch.info Lines: 100.00 % 5 0
Test Date: 2026-04-28 11:55:42 Functions: - 0 0
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 100.00 % 4 0

             Branch data     Line data    Source code
       1                 :             : // -*- mode: C++; c-file-style: "cc-mode" -*-
       2                 :             : //*************************************************************************
       3                 :             : // DESCRIPTION: Verilator: Collect and print statistics
       4                 :             : //
       5                 :             : // Code available from: https://verilator.org
       6                 :             : //
       7                 :             : //*************************************************************************
       8                 :             : //
       9                 :             : // This program is free software; you can redistribute it and/or modify it
      10                 :             : // under the terms of either the GNU Lesser General Public License Version 3
      11                 :             : // or the Perl Artistic License Version 2.0.
      12                 :             : // SPDX-FileCopyrightText: 2005-2026 Wilson Snyder
      13                 :             : // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
      14                 :             : //
      15                 :             : //*************************************************************************
      16                 :             : 
      17                 :             : #include "V3PchAstNoMT.h"  // VL_MT_DISABLED_CODE_UNIT
      18                 :             : 
      19                 :             : #include "V3Assert.h"
      20                 :             : 
      21                 :             : #include "V3AstUserAllocator.h"
      22                 :             : #include "V3Stats.h"
      23                 :             : 
      24                 :             : VL_DEFINE_DEBUG_FUNCTIONS;
      25                 :             : 
      26                 :             : //######################################################################
      27                 :             : // AssertDeFutureVisitor
      28                 :             : // If any AstFuture, then move all non-future varrefs to be one cycle behind,
      29                 :             : // see IEEE 1800-2023 16.9.4.
      30                 :             : 
      31                 :             : class AssertDeFutureVisitor final : public VNVisitor {
      32                 :             :     // STATE - across all visitors
      33                 :             :     AstNodeModule* const m_modp;  // Module future is underneath
      34                 :             :     const AstFuture* m_futurep;  // First AstFuture found
      35                 :             :     const unsigned m_pastNum;  // Prefix unique number for this module
      36                 :             :     std::map<AstVar*, AstVar*> m_delayedVars;  // Old to delayed variable mapping
      37                 :             :     // STATE - for current visit position (use VL_RESTORER)
      38                 :             :     bool m_inFuture = false;  // Inside a future
      39                 :             :     bool m_unsupported = false;  // Printed unsupported
      40                 :             : 
      41                 :             :     // METHODS
      42                 :             :     void unsupported(AstNode* nodep) {
      43                 :             :         if (m_unsupported) return;
      44                 :             :         m_unsupported = true;
      45                 :             :         nodep->v3warn(E_UNSUPPORTED,
      46                 :             :                       "Unsupported/illegal: Future value function used with expression with "
      47                 :             :                           << nodep->prettyOperatorName());
      48                 :             :     }
      49                 :             :     // VISITORS
      50                 :             :     void visit(AstFuture* nodep) override {
      51                 :             :         VL_RESTORER(m_inFuture);
      52                 :             :         m_inFuture = true;
      53                 :             :         iterateChildren(nodep);
      54                 :             :         // Done with the future, this subexpression is current-time
      55                 :             :         nodep->replaceWith(nodep->exprp()->unlinkFrBack());
      56                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
      57                 :             :     }
      58                 :             :     void visit(AstNodeVarRef* nodep) override {
      59                 :             :         if (nodep->user1SetOnce()) return;
      60                 :             :         if (m_inFuture || m_unsupported)
      61                 :             :             return;  // Need user1 set above, don't process when Future is removed
      62                 :             :         if (nodep->access().isWriteOrRW()) {
      63                 :             :             unsupported(nodep);
      64                 :             :             return;
      65                 :             :         }
      66                 :             :         auto it = m_delayedVars.find(nodep->varp());
      67                 :             :         AstVar* outvarp;
      68                 :             :         if (it == m_delayedVars.end()) {
      69                 :             :             AstSenTree* const sentreep = m_futurep->sentreep();
      70                 :             :             AstAlways* const alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS,
      71                 :             :                                                      sentreep->cloneTree(false), nullptr};
      72                 :             :             m_modp->addStmtsp(alwaysp);
      73                 :             :             outvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
      74                 :             :                                  "__Vnotfuture" + cvtToStr(m_pastNum) + "_" + nodep->name(),
      75                 :             :                                  nodep->dtypep()};
      76                 :             :             m_modp->addStmtsp(outvarp);
      77                 :             :             AstVarRef* varRefAWritep = new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE};
      78                 :             :             varRefAWritep->user1(true);
      79                 :             :             AstNodeVarRef* varRefAReadp = nodep->cloneTree(false);
      80                 :             :             varRefAReadp->user1(true);
      81                 :             :             AstNode* const assp = new AstAssignDly{nodep->fileline(), varRefAWritep, varRefAReadp};
      82                 :             :             alwaysp->addStmtsp(assp);
      83                 :             :             m_delayedVars.emplace(nodep->varp(), outvarp);
      84                 :             :         } else {
      85                 :             :             outvarp = it->second;
      86                 :             :         }
      87                 :             :         AstVarRef* newp = new AstVarRef{nodep->fileline(), outvarp, VAccess::READ};
      88                 :             :         newp->user1(true);
      89                 :             :         UINFO(9, "DeFuture " << nodep << "  becomes " << newp);
      90                 :             :         nodep->replaceWith(newp);
      91                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
      92                 :             :     }
      93                 :             :     void visit(AstNodeFTaskRef* nodep) override { unsupported(nodep); }
      94                 :             :     void visit(AstMethodCall* nodep) override { unsupported(nodep); }
      95                 :             :     void visit(AstNode* nodep) override {
      96                 :             :         if (!nodep->isPure()) unsupported(nodep);
      97                 :             :         iterateChildren(nodep);
      98                 :             :     }
      99                 :             : 
     100                 :             : public:
     101                 :             :     // CONSTRUCTORS
     102                 :             :     explicit AssertDeFutureVisitor(AstNode* nodep, AstNodeModule* modp, unsigned pastNum)
     103                 :             :         : m_modp{modp}
     104                 :             :         , m_pastNum{pastNum} {
     105                 :             :         // See if any Future before we process
     106                 :             :         if (nodep->forall([&](const AstFuture* futurep) -> bool {
     107                 :             :                 m_futurep = futurep;
     108                 :             :                 return false;
     109                 :             :             }))
     110                 :             :             return;
     111                 :             :         // UINFOTREE(9, nodep, "", "defuture-in");
     112                 :             :         visit(nodep);  // Nodep may get deleted
     113                 :             :         // UINFOTREE(9, nodep, "", "defuture-ou");
     114                 :             :     }
     115                 :             :     ~AssertDeFutureVisitor() = default;
     116                 :             : };
     117                 :             : 
     118                 :             : //######################################################################
     119                 :             : // AssertVisitor
     120                 :             : 
     121                 :             : class AssertVisitor final : public VNVisitor {
     122                 :             :     // CONSTANTS
     123                 :             :     static constexpr uint8_t ALL_ASSERT_TYPES
     124                 :             :         = std::numeric_limits<std::underlying_type<VAssertType::en>::type>::max();
     125                 :             : 
     126                 :             :     // NODE STATE/TYPES
     127                 :             :     // Cleared on netlist
     128                 :             :     //  AstNode::user1()         -> bool.  True if processed
     129                 :             :     //  AstAlways::user2p()      -> std::vector<AstVar*>. Delayed variables via 'm_delayed'
     130                 :             :     //  AstNodeVarRef::user2()   -> bool.  True if shouldn't be sampled
     131                 :             :     const VNUser1InUse m_user1InUse;
     132                 :             :     const VNUser2InUse m_user2InUse;
     133                 :             :     AstUser2Allocator<AstAlways, std::vector<AstVar*>> m_delayed;
     134                 :             : 
     135                 :             :     // STATE
     136                 :             :     AstNodeModule* m_modp = nullptr;  // Last module
     137                 :             :     const AstNode* m_beginp = nullptr;  // Last AstBegin/AstGenBlock
     138                 :             :     unsigned m_monitorNum = 0;  // Global $monitor numbering (not per module)
     139                 :             :     AstVar* m_monitorNumVarp = nullptr;  // $monitor number variable
     140                 :             :     AstVar* m_monitorOffVarp = nullptr;  // $monitoroff variable
     141                 :             :     unsigned m_modPastNum = 0;  // Module past numbering
     142                 :             :     unsigned m_modStrobeNum = 0;  // Module $strobe numbering
     143                 :             :     AstNodeProcedure* m_procedurep = nullptr;  // Current procedure
     144                 :             :     VDouble0 m_statCover;  // Statistic tracking
     145                 :             :     VDouble0 m_statAsNotImm;  // Statistic tracking
     146                 :             :     VDouble0 m_statAsImm;  // Statistic tracking
     147                 :             :     VDouble0 m_statAsFull;  // Statistic tracking
     148                 :             :     VDouble0 m_statPastVars;  // Statistic tracking
     149                 :             :     bool m_inSampled = false;  // True inside a sampled expression
     150                 :             :     bool m_inRestrict = false;  // True inside restrict assertion
     151                 :             :     AstNode* m_passsp = nullptr;  // Current pass statement
     152                 :             :     AstNode* m_failsp = nullptr;  // Current fail statement
     153                 :             :     AstFinal* m_finalp = nullptr;  // Current final block
     154                 :             :     // Map from (expression, senTree) to AstAlways that computes delayed values of the expression
     155                 :             :     std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
     156                 :             :         m_modExpr2Sen2DelayedAlwaysp;
     157                 :             : 
     158                 :             :     // METHODS
     159                 :             :     static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
     160                 :             :                                      VAssertDirectiveType directiveType) {
     161                 :             :         // cppcheck-suppress missingReturn
     162                 :             :         switch (directiveType) {
     163                 :             :         case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
     164                 :             :         case VAssertDirectiveType::VIOLATION_CASE: {
     165                 :             :             if (v3Global.opt.assertCase()) {
     166                 :             :                 return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
     167                 :             :                                     1};
     168                 :             :             }
     169                 :             :             // If assertions are off, have constant propagation rip them out later
     170                 :             :             // This allows syntax errors and such to be detected normally.
     171                 :             :             return new AstConst{fl, AstConst::BitFalse{}};
     172                 :             :         }
     173                 :             :         case VAssertDirectiveType::ASSERT:
     174                 :             :         case VAssertDirectiveType::COVER:
     175                 :             :         case VAssertDirectiveType::ASSUME: {
     176                 :             :             if (v3Global.opt.assertOn()) {
     177                 :             :                 return new AstCExpr{fl, AstCExpr::Pure{},
     178                 :             :                                     "vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
     179                 :             :                                         + ", "s + std::to_string(directiveType) + ")"s,
     180                 :             :                                     1};
     181                 :             :             }
     182                 :             :             return new AstConst{fl, AstConst::BitFalse{}};
     183                 :             :         }
     184                 :             :         case VAssertDirectiveType::INTERNAL:
     185                 :             :         case VAssertDirectiveType::VIOLATION_IF:
     186                 :             :         case VAssertDirectiveType::RESTRICT: {
     187                 :             :             if (v3Global.opt.assertOn()) {
     188                 :             :                 return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
     189                 :             :                                     1};
     190                 :             :             }
     191                 :             :             return new AstConst{fl, AstConst::BitFalse{}};
     192                 :             :         }
     193                 :             :         }
     194                 :             :         VL_UNREACHABLE;
     195                 :             :     }
     196                 :             :     string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
     197                 :             :                                 VDisplayType severity) {
     198                 :             :         if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
     199                 :             :             return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
     200                 :             :                     + cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
     201                 :             :                     + ((message != "") ? ": " : "") + message + "\n");
     202                 :             :         }
     203                 :             :         return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
     204                 :             :                 + cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
     205                 :             :                 + message + "\n");
     206                 :             :     }
     207                 :             :     static bool resolveAssertType(AstAssertCtl* nodep) {
     208                 :             :         if (!nodep->assertTypesp()) {
     209                 :             :             nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
     210                 :             :             return true;
     211                 :             :         }
     212                 :             :         if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
     213                 :             :             nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
     214                 :             :             return true;
     215                 :             :         }
     216                 :             :         return false;
     217                 :             :     }
     218                 :             :     static bool resolveControlType(AstAssertCtl* nodep) {
     219                 :             :         if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
     220                 :             :             nodep->ctlType(constp->toSInt());
     221                 :             :             return true;
     222                 :             :         }
     223                 :             :         return false;
     224                 :             :     }
     225                 :             :     static bool resolveDirectiveType(AstAssertCtl* nodep) {
     226                 :             :         if (!nodep->directiveTypesp()) {
     227                 :             :             nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
     228                 :             :                                      | VAssertDirectiveType::COVER);
     229                 :             :             return true;
     230                 :             :         }
     231                 :             :         if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
     232                 :             :             nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
     233                 :             :             return true;
     234                 :             :         }
     235                 :             :         return false;
     236                 :             :     }
     237                 :             :     void replaceDisplay(AstDisplay* nodep, const string& prefix) {
     238                 :             :         nodep->fmtp()->text(
     239                 :             :             assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
     240                 :             :         nodep->displayType(VDisplayType::DT_WRITE);
     241                 :             :         // cppcheck-suppress nullPointer
     242                 :             :         AstNodeExpr* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
     243                 :             :         if (AstNodeExpr* const timesp = nodep->fmtp()->exprsp()) {
     244                 :             :             timesp->unlinkFrBackWithNext();
     245                 :             :             timenewp->addNext(timesp);
     246                 :             :         }
     247                 :             :         nodep->fmtp()->addExprsp(timenewp);
     248                 :             :         if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
     249                 :             :             nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
     250                 :             :         }
     251                 :             :     }
     252                 :             :     AstSampled* newSampledExpr(AstNodeExpr* nodep) {
     253                 :             :         AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep};
     254                 :             :         sampledp->dtypeFrom(nodep);
     255                 :             :         return sampledp;
     256                 :             :     }
     257                 :             :     AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
     258                 :             :         if (!m_monitorNumVarp) {
     259                 :             :             m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
     260                 :             :                                           nodep->findUInt64DType()};
     261                 :             :             v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
     262                 :             :         }
     263                 :             :         AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
     264                 :             :         varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
     265                 :             :         return varrefp;
     266                 :             :     }
     267                 :             :     AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
     268                 :             :         if (!m_monitorOffVarp) {
     269                 :             :             m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
     270                 :             :                                           nodep->findBitDType()};
     271                 :             :             v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
     272                 :             :         }
     273                 :             :         AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
     274                 :             :         varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
     275                 :             :         return varrefp;
     276                 :             :     }
     277                 :             :     static AstNodeStmt* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
     278                 :             :                                       VAssertType type = VAssertType::INTERNAL) {
     279                 :             :         // Add a internal if to check assertions are on.
     280                 :             :         // Don't make this a AND term, as it's unlikely to need to test this.
     281                 :             :         FileLine* const fl = bodyp->fileline();
     282                 :             : 
     283                 :             :         AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
     284                 :             :         AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
     285                 :             :         newp->isBoundsCheck(true);  // To avoid LATCH warning
     286                 :             :         newp->user1(true);  // Don't assert/cover this if
     287                 :             :         return newp;
     288                 :             :     }
     289                 :             : 
     290                 :             :     static AstIf* assertCond(const AstNodeCoverOrAssert* nodep, AstNodeExpr* propp,
     291                 :             :                              AstNode* passsp, AstNode* failsp) {
     292                 :             :         AstIf* const ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
     293                 :             :         // It's more LIKELY that we'll take the nullptr if clause
     294                 :             :         // than the sim-killing else clause:
     295                 :             :         ifp->branchPred(VBranchPred::BP_LIKELY);
     296                 :             :         ifp->isBoundsCheck(true);  // To avoid LATCH warning
     297                 :             :         return ifp;
     298                 :             :     }
     299                 :             : 
     300                 :             :     AstNode* assertBody(const AstNodeCoverOrAssert* nodep, AstNode* propp, AstNode* passsp,
     301                 :             :                         AstNode* failsp) {
     302                 :             :         AstNode* bodyp = nullptr;
     303                 :             :         if (AstPExpr* const pexprp = VN_CAST(propp, PExpr)) {
     304                 :             :             AstFork* const forkp = new AstFork{nodep->fileline(), VJoinType::JOIN_NONE};
     305                 :             :             forkp->addForksp(pexprp->bodyp()->unlinkFrBack());
     306         [ +  + ]:          27 :             if (AstNodeStmt* const finalp = pexprp->finalp()) {
     307         [ +  + ]:           7 :                 if (!m_finalp) {
     308                 :           3 :                     m_finalp = new AstFinal{m_modp->fileline(), finalp->unlinkFrBack()};
     309                 :           3 :                     m_modp->addStmtsp(m_finalp);
     310                 :             :                 } else {
     311                 :           4 :                     m_finalp->addStmtsp(finalp->unlinkFrBack());
     312                 :             :                 }
     313                 :             :             }
     314                 :             :             VL_DO_DANGLING2(pushDeletep(pexprp), pexprp, propp);
     315                 :             :             bodyp = forkp;
     316                 :             :         } else {
     317                 :             :             bodyp = assertCond(nodep, VN_AS(propp, NodeExpr), passsp, failsp);
     318                 :             :         }
     319                 :             :         return newIfAssertOn(bodyp, nodep->directive(), nodep->userType());
     320                 :             :     }
     321                 :             : 
     322                 :             :     AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
     323                 :             :                                         AstNodeExpr* exprsp = nullptr) {
     324                 :             :         // Like newFireAssert() but omits the asserts-on check
     325                 :             :         AstDisplay* const dispp
     326                 :             :             = new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
     327                 :             :         dispp->fmtp()->timeunit(m_modp->timeunit());
     328                 :             :         AstNodeStmt* const bodysp = dispp;
     329                 :             :         replaceDisplay(dispp, "%%Error");  // Convert to standard DISPLAY format
     330                 :             :         if (exprsp) dispp->fmtp()->exprsp()->addNext(exprsp);
     331                 :             :         if (v3Global.opt.stopFail()) bodysp->addNext(new AstStop{nodep->fileline(), false});
     332                 :             :         return bodysp;
     333                 :             :     }
     334                 :             : 
     335                 :             :     AstNodeStmt* newFireAssert(const AstNodeStmt* nodep, VAssertDirectiveType directiveType,
     336                 :             :                                VAssertType assertType, const string& message,
     337                 :             :                                AstNodeExpr* exprsp = nullptr) {
     338                 :             :         AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
     339                 :             :         bodysp = newIfAssertOn(bodysp, directiveType, assertType);
     340                 :             :         return bodysp;
     341                 :             :     }
     342                 :             : 
     343                 :             :     AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp) {
     344                 :             :         FileLine* const flp = exprp->fileline();
     345                 :             :         AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, exprp->dtypep()};
     346                 :             :         // TODO: this lifetime seems nonsene (can't have NBAs to automatics), but is as before
     347                 :             :         varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
     348                 :             :         m_modp->addStmtsp(varp);
     349                 :             :         ++m_statPastVars;
     350                 :             :         // Actually set the delayed value
     351                 :             :         AstNodeExpr* const lhsp = new AstVarRef{flp, varp, VAccess::WRITE};
     352                 :             :         AstAssignDly* const assignp = new AstAssignDly{flp, lhsp, exprp};
     353                 :             :         if (!alwaysp->stmtsp()) {
     354                 :             :             alwaysp->addStmtsp(assignp);
     355                 :             :         } else {
     356                 :             :             alwaysp->stmtsp()->addHereThisAsNext(assignp);
     357                 :             :         }
     358                 :             :         return varp;
     359                 :             :     }
     360                 :             : 
     361                 :             :     AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstSenTree* senTreep) {
     362                 :             :         AstAlways*& alwayspr = m_modExpr2Sen2DelayedAlwaysp[*exprp][*senTreep];
     363                 :             :         if (!alwayspr) {
     364                 :             :             FileLine* const flp = exprp->fileline();
     365                 :             :             // Create the always block that computes the delayed values
     366                 :             :             alwayspr = new AstAlways{flp, VAlwaysKwd::ALWAYS, senTreep, nullptr};
     367                 :             :             m_modp->addStmtsp(alwayspr);
     368                 :             :             // Create the once-delayed variable
     369                 :             :             const std::string name = "_Vpast_" + cvtToStr(m_modPastNum++) + "_1";
     370                 :             :             AstVar* const varp = createDelayedVar(name, alwayspr, exprp);
     371                 :             :             // Add it to delayed variable vector
     372                 :             :             m_delayed(alwayspr).emplace_back(varp);
     373                 :             :         } else {
     374                 :             :             // Reusing exiting, not needed
     375                 :             :             VL_DO_DANGLING(pushDeletep(exprp), exprp);
     376                 :             :             VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
     377                 :             :         }
     378                 :             :         return alwayspr;
     379                 :             :     }
     380                 :             : 
     381                 :             :     AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstSenTree* senTreep, uint32_t ticks) {
     382                 :             :         UASSERT_OBJ(ticks > 0, exprp, "Delay must be > 0");
     383                 :             :         AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
     384                 :             :         std::vector<AstVar*>& delayedr = m_delayed(alwaysp);
     385                 :             :         // Ensure the required delay exists
     386                 :             :         while (delayedr.size() < ticks) {
     387                 :             :             AstVar* const firstp = delayedr.front();
     388                 :             :             FileLine* const flp = firstp->fileline();
     389                 :             :             // Create once more delayed value
     390                 :             :             std::string name = firstp->name();
     391                 :             :             name.resize(name.size() - 1);
     392                 :             :             name += std::to_string(delayedr.size() + 1);
     393                 :             :             AstNodeExpr* const prevp = new AstVarRef{flp, delayedr.back(), VAccess::READ};
     394                 :             :             AstVar* const varp = createDelayedVar(name, alwaysp, prevp);
     395                 :             :             // Add it to delayed variable vector
     396                 :             :             delayedr.emplace_back(varp);
     397                 :             :         }
     398                 :             :         // Return a reference to the appropriately delayed variable
     399                 :             :         return new AstVarRef{exprp->fileline(), delayedr.at(ticks - 1), VAccess::READ};
     400                 :             :     }
     401                 :             : 
     402                 :             :     void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
     403                 :             :         if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
     404                 :             : 
     405                 :             :         { AssertDeFutureVisitor{nodep->propp(), m_modp, m_modPastNum++}; }
     406                 :             : 
     407                 :             :         iterateAndNextNull(nodep->sentreep());
     408                 :             :         if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
     409                 :             :             iterateAndNextNull(assertp->failsp());
     410                 :             :         } else if (AstAssertIntrinsic* const assertp = VN_CAST(nodep, AssertIntrinsic)) {
     411                 :             :             iterateAndNextNull(assertp->failsp());
     412                 :             :         } else if (AstCover* const coverp = VN_CAST(nodep, Cover)) {
     413                 :             :             iterateAndNextNull(coverp->coverincsp());
     414                 :             :         } else if (!VN_IS(nodep, Restrict)) {
     415                 :             :             nodep->v3fatalSrc("Unhandled assert type");
     416                 :             :         }
     417                 :             :         iterateAndNextNull(nodep->passsp());
     418                 :             :         AstSenTree* const sentreep = nodep->sentreep();
     419                 :             :         if (nodep->immediate()) {
     420                 :             :             UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
     421                 :             :         } else {
     422                 :             :             UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
     423                 :             :             if (m_procedurep) {
     424                 :             :                 if (!nodep->senFromAlways()) {
     425                 :             :                     // To support this need queue of asserts to activate
     426                 :             :                     nodep->v3warn(E_UNSUPPORTED,
     427                 :             :                                   "Unsupported: Procedural concurrent assertion with"
     428                 :             :                                   " clocking event inside always (IEEE 1800-2023 16.14.6)");
     429                 :             :                 }
     430                 :             :                 // Change type to concurrent and relink after process
     431                 :             :                 nodep->immediate(false);
     432                 :             :                 static_cast<AstNode*>(m_procedurep)->addNext(nodep->unlinkFrBack());
     433                 :             :                 return;  // Later iterate will pick up
     434                 :             :             }
     435                 :             :             sentreep->unlinkFrBack();
     436                 :             :         }
     437                 :             :         //
     438                 :             :         const string& message = nodep->name();
     439                 :             :         AstNode* passsp = nodep->passsp();
     440                 :             :         if (passsp) passsp->unlinkFrBackWithNext();
     441                 :             :         if (failsp) failsp->unlinkFrBackWithNext();
     442                 :             : 
     443                 :             :         bool selfDestruct = false;
     444                 :             :         if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
     445                 :             :             ++m_statCover;
     446                 :             :             if (!v3Global.opt.coverageUser()) {
     447                 :             :                 selfDestruct = true;
     448                 :             :             } else {
     449                 :             :                 // V3Coverage assigned us a bucket to increment.
     450                 :             :                 AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
     451                 :             :                 UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
     452                 :             :                 covincp->unlinkFrBackWithNext();  // next() might have  AstAssign for trace
     453                 :             :                 if (message != "") covincp->declp()->comment(message);
     454                 :             :                 if (passsp) {
     455                 :             :                     passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
     456                 :             :                 } else {
     457                 :             :                     passsp = covincp;
     458                 :             :                 }
     459                 :             :             }
     460                 :             :         } else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
     461                 :             :             if (nodep->immediate()) {
     462                 :             :                 ++m_statAsImm;
     463                 :             :             } else {
     464                 :             :                 ++m_statAsNotImm;
     465                 :             :             }
     466                 :             :             if (!passsp && !failsp)
     467                 :             :                 failsp = newFireAssertUnchecked(
     468                 :             :                     nodep, VN_IS(nodep, AssertIntrinsic) ? "'$cast' failed." : "'assert' failed.");
     469                 :             :         } else {
     470                 :             :             nodep->v3fatalSrc("Unknown node type");
     471                 :             :         }
     472                 :             : 
     473                 :             :         VL_RESTORER(m_passsp);
     474                 :             :         VL_RESTORER(m_failsp);
     475                 :             :         m_passsp = passsp;
     476                 :             :         m_failsp = failsp;
     477                 :             :         iterate(nodep->propp());
     478                 :             : 
     479                 :             :         AstNode* propExprp;
     480                 :             :         AstNodeExpr* disablep = nullptr;
     481                 :             :         if (AstPropSpec* const specp = VN_CAST(nodep->propp(), PropSpec)) {
     482                 :             :             propExprp = specp->propp()->unlinkFrBack();
     483                 :             :             if (specp->disablep()) disablep = specp->disablep()->unlinkFrBack();
     484                 :             :         } else {
     485                 :             :             propExprp = nodep->propp()->unlinkFrBack();
     486                 :             :         }
     487                 :             :         AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
     488                 :             :         if (disablep) {
     489                 :             :             bodysp
     490                 :             :                 = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), disablep}, bodysp};
     491                 :             :         }
     492                 :             :         if (sentreep) {
     493                 :             :             bodysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
     494                 :             :         }
     495                 :             : 
     496                 :             :         if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
     497                 :             :         if (failsp && !failsp->backp()) VL_DO_DANGLING(pushDeletep(failsp), failsp);
     498                 :             : 
     499                 :             :         // Install it
     500                 :             :         if (selfDestruct) {
     501                 :             :             // Delete it after making the tree.  This way we can tell the user
     502                 :             :             // if it wasn't constructed nicely or has other errors without needing --coverage.
     503                 :             :             VL_DO_DANGLING(bodysp->deleteTree(), bodysp);
     504                 :             :             nodep->unlinkFrBack();
     505                 :             :         } else {
     506                 :             :             nodep->replaceWith(bodysp);
     507                 :             :         }
     508                 :             :         // Bye
     509                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     510                 :             :     }
     511                 :             : 
     512                 :             :     // VISITORS
     513                 :             :     void visit(AstIf* nodep) override {
     514                 :             :         if (nodep->user1SetOnce()) return;
     515                 :             :         if (nodep->uniquePragma() || nodep->unique0Pragma()) {
     516                 :             :             const AstNodeIf* ifp = nodep;
     517                 :             :             AstNodeExpr* propp = nullptr;
     518                 :             :             bool hasDefaultElse = false;
     519                 :             :             do {
     520                 :             :                 // If this statement ends with 'else if', then nextIf will point to the
     521                 :             :                 // nextIf statement.  Otherwise it will be null.
     522                 :             :                 const AstNodeIf* const nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
     523                 :             :                 iterateAndNextNull(ifp->condp());
     524                 :             : 
     525                 :             :                 // Recurse into the true case.
     526                 :             :                 iterateAndNextNull(ifp->thensp());
     527                 :             : 
     528                 :             :                 // If the last else is not an else if, recurse into that too.
     529                 :             :                 if (ifp->elsesp() && !nextifp) {  //
     530                 :             :                     iterateAndNextNull(ifp->elsesp());
     531                 :             :                 }
     532                 :             : 
     533                 :             :                 // Build a bitmask of the true predicates
     534                 :             :                 AstNodeExpr* const predp = ifp->condp()->cloneTreePure(false);
     535                 :             :                 if (propp) {
     536                 :             :                     propp = new AstConcat{nodep->fileline(), predp, propp};
     537                 :             :                 } else {
     538                 :             :                     propp = predp;
     539                 :             :                 }
     540                 :             : 
     541                 :             :                 // Record if this ends with an 'else' that does not have an if
     542                 :             :                 if (ifp->elsesp() && !nextifp) hasDefaultElse = true;
     543                 :             : 
     544                 :             :                 ifp = nextifp;
     545                 :             :             } while (ifp);
     546                 :             : 
     547                 :             :             AstIf* const newifp = nodep->cloneTree(false);
     548                 :             :             const bool allow_none = nodep->unique0Pragma();
     549                 :             : 
     550                 :             :             // Empty case means no property
     551                 :             :             if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
     552                 :             : 
     553                 :             :             // Note: if this ends with an 'else', then we don't need to validate that one of the
     554                 :             :             // predicates evaluates to true.
     555                 :             :             AstNodeExpr* const ohot
     556                 :             :                 = ((allow_none || hasDefaultElse)
     557                 :             :                        ? static_cast<AstNodeExpr*>(new AstOneHot0{nodep->fileline(), propp})
     558                 :             :                        : static_cast<AstNodeExpr*>(new AstOneHot{nodep->fileline(), propp}));
     559                 :             :             const VAssertType assertType
     560                 :             :                 = nodep->uniquePragma() ? VAssertType::UNIQUE : VAssertType::UNIQUE0;
     561                 :             :             AstIf* const checkifp
     562                 :             :                 = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
     563                 :             :                             newFireAssert(nodep, VAssertDirectiveType::VIOLATION_IF, assertType,
     564                 :             :                                           "'unique if' statement violated"),
     565                 :             :                             newifp};
     566                 :             :             checkifp->isBoundsCheck(true);  // To avoid LATCH warning
     567                 :             :             checkifp->branchPred(VBranchPred::BP_UNLIKELY);
     568                 :             :             nodep->replaceWith(checkifp);
     569                 :             :             VL_DO_DANGLING(pushDeletep(nodep), nodep);
     570                 :             :         } else {
     571                 :             :             iterateChildren(nodep);
     572                 :             :         }
     573                 :             :     }
     574                 :             : 
     575                 :             :     //========== Case assertions
     576                 :             :     void visit(AstCase* nodep) override {
     577                 :             :         iterateChildren(nodep);
     578                 :             :         if (!nodep->user1SetOnce()) {
     579                 :             :             bool has_default = false;
     580                 :             :             for (AstCaseItem* itemp = nodep->itemsp(); itemp;
     581                 :             :                  itemp = VN_AS(itemp->nextp(), CaseItem)) {
     582                 :             :                 if (itemp->isDefault()) has_default = true;
     583                 :             :             }
     584                 :             :             const AstNodeDType* exprDtypep = nodep->exprp()->dtypep()->skipRefp();
     585                 :             : 
     586                 :             :             VAssertType assertType = VAssertType::INTERNAL;
     587                 :             :             if (nodep->priorityPragma()) {
     588                 :             :                 assertType = VAssertType::PRIORITY;
     589                 :             :             } else if (nodep->uniquePragma()) {
     590                 :             :                 assertType = VAssertType::UNIQUE;
     591                 :             :             } else if (nodep->unique0Pragma()) {
     592                 :             :                 assertType = VAssertType::UNIQUE0;
     593                 :             :             }
     594                 :             : 
     595                 :             :             string valFmt;
     596                 :             :             if (exprDtypep->isIntegralOrPacked())
     597                 :             :                 valFmt = " for '" + cvtToStr(exprDtypep->widthMin()) + "'h%X'";
     598                 :             :             if (nodep->fullPragma() || nodep->priorityPragma()) {
     599                 :             :                 // Need to add a default if there isn't one already
     600                 :             :                 ++m_statAsFull;
     601                 :             :                 if (!has_default) {
     602                 :             :                     nodep->addItemsp(new AstCaseItem{
     603                 :             :                         nodep->fileline(), nullptr /*DEFAULT*/,
     604                 :             :                         newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
     605                 :             :                                       nodep->pragmaString() + ", but non-match found" + valFmt,
     606                 :             :                                       valFmt.empty() ? nullptr
     607                 :             :                                                      : nodep->exprp()->cloneTreePure(false))});
     608                 :             :                 }
     609                 :             :             }
     610                 :             :             if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
     611                 :             :                 // Need to check that one, and only one of the case items match at any moment
     612                 :             :                 // If there's a default, we allow none to match, else exactly one must match
     613                 :             :                 ++m_statAsFull;
     614                 :             :                 if (!has_default && !nodep->itemsp()) {
     615                 :             :                     // Not parallel, but harmlessly so.
     616                 :             :                 } else {
     617                 :             :                     AstNodeExpr* propp = nullptr;
     618                 :             :                     for (AstCaseItem* itemp = nodep->itemsp(); itemp;
     619                 :             :                          itemp = VN_AS(itemp->nextp(), CaseItem)) {
     620                 :             :                         AstNodeExpr* itembitp = nullptr;
     621                 :             :                         for (AstNodeExpr* icondp = itemp->condsp(); icondp;
     622                 :             :                              icondp = VN_AS(icondp->nextp(), NodeExpr)) {
     623                 :             :                             AstNodeExpr* onep;
     624                 :             :                             if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
     625                 :             :                                 onep = rcondp->newAndFromInside(
     626                 :             :                                     nodep->exprp()->cloneTreePure(true),
     627                 :             :                                     rcondp->lhsp()->cloneTreePure(true),
     628                 :             :                                     rcondp->rhsp()->cloneTreePure(true));
     629                 :             :                             } else if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
     630                 :             :                                 onep = AstEqWild::newTyped(itemp->fileline(),
     631                 :             :                                                            nodep->exprp()->cloneTreePure(false),
     632                 :             :                                                            icondp->cloneTreePure(false));
     633                 :             :                             } else {
     634                 :             :                                 onep = AstEq::newTyped(icondp->fileline(),
     635                 :             :                                                        nodep->exprp()->cloneTreePure(false),
     636                 :             :                                                        icondp->cloneTreePure(false));
     637                 :             :                             }
     638                 :             :                             // OR together all conditions within the same case item
     639                 :             :                             if (onep) {
     640                 :             :                                 if (itembitp) {
     641                 :             :                                     itembitp = new AstOr{icondp->fileline(), onep, itembitp};
     642                 :             :                                 } else {
     643                 :             :                                     itembitp = onep;
     644                 :             :                                 }
     645                 :             :                             }
     646                 :             :                         }
     647                 :             :                         if (itembitp) {
     648                 :             :                             if (propp) {
     649                 :             :                                 propp = new AstConcat{itemp->fileline(), itembitp, propp};
     650                 :             :                             } else {
     651                 :             :                                 propp = itembitp;
     652                 :             :                             }
     653                 :             :                         }
     654                 :             :                     }
     655                 :             :                     // Empty case means no property
     656                 :             :                     if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
     657                 :             :                     const bool allow_none = has_default || nodep->unique0Pragma();
     658                 :             :                     // The following assertion looks as below.
     659                 :             :                     // if (!$onehot(propp)) begin
     660                 :             :                     //     if (propp == '0) begin if (!allow_none) $error("none match"); end
     661                 :             :                     //     else $error("multiple match");
     662                 :             :                     // end
     663                 :             :                     AstNodeExpr* const ohot = new AstOneHot{nodep->fileline(), propp};
     664                 :             :                     AstConst* const zero = new AstConst{
     665                 :             :                         nodep->fileline(), AstConst::WidthedValue{}, propp->width(), 0};
     666                 :             :                     AstIf* const ohotIfp
     667                 :             :                         = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}};
     668                 :             :                     AstIf* const zeroIfp = new AstIf{
     669                 :             :                         nodep->fileline(),
     670                 :             :                         new AstEq{nodep->fileline(), propp->cloneTreePure(false), zero}};
     671                 :             :                     AstNodeExpr* const exprp = nodep->exprp();
     672                 :             :                     const string pragmaStr = nodep->pragmaString();
     673                 :             :                     if (!allow_none)
     674                 :             :                         zeroIfp->addThensp(
     675                 :             :                             newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
     676                 :             :                                           pragmaStr + ", but none matched" + valFmt,
     677                 :             :                                           valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
     678                 :             :                     zeroIfp->addElsesp(
     679                 :             :                         newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
     680                 :             :                                       pragmaStr + ", but multiple matches found" + valFmt,
     681                 :             :                                       valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
     682                 :             :                     ohotIfp->addThensp(zeroIfp);
     683                 :             :                     ohotIfp->isBoundsCheck(true);  // To avoid LATCH warning
     684                 :             :                     ohotIfp->branchPred(VBranchPred::BP_UNLIKELY);
     685                 :             :                     nodep->addNotParallelp(ohotIfp);
     686                 :             :                 }
     687                 :             :             }
     688                 :             :         }
     689                 :             :     }
     690                 :             : 
     691                 :             :     void visit(AstFuture* nodep) override {
     692                 :             :         nodep->v3error("Future sampled value function called outside property or sequence "
     693                 :             :                        "expression (IEEE 16.9.4)");
     694                 :             :         nodep->replaceWith(new AstConst{nodep->fileline(), 0});
     695                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     696                 :             :     }
     697                 :             : 
     698                 :             :     //========== Past
     699                 :             :     void visit(AstPast* nodep) override {
     700                 :             :         iterateChildren(nodep);
     701                 :             :         uint32_t ticks = 1;
     702                 :             :         if (nodep->ticksp()) {
     703                 :             :             UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
     704                 :             :                         "Expected constant ticks, checked in V3Width");
     705                 :             :             ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
     706                 :             :         }
     707                 :             :         UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
     708                 :             :         AstNodeExpr* const exprp = newSampledExpr(nodep->exprp()->unlinkFrBack());
     709                 :             :         AstNodeExpr* inp = getPastValue(exprp, nodep->sentreep()->unlinkFrBack(), ticks);
     710                 :             :         nodep->replaceWith(inp);
     711                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     712                 :             :     }
     713                 :             : 
     714                 :             :     //========== Move $sampled down to read-only variables
     715                 :             :     void visit(AstSampled* nodep) override {
     716                 :             :         if (nodep->user1()) return;
     717                 :             :         VL_RESTORER(m_inSampled);
     718                 :             :         {
     719                 :             :             m_inSampled = true;
     720                 :             :             iterateChildren(nodep);
     721                 :             :         }
     722                 :             :         if (nodep->exprp()) {
     723                 :             :             nodep->replaceWith(nodep->exprp()->unlinkFrBack());
     724                 :             :         } else {
     725                 :             :             nodep->unlinkFrBack();
     726                 :             :         }
     727                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     728                 :             :     }
     729                 :             :     void visit(AstNodeVarRef* nodep) override {
     730                 :             :         if (m_inSampled && !nodep->user2() && !nodep->varp()->isTemp()) {
     731                 :             :             if (!nodep->access().isReadOnly()) {
     732                 :             :                 nodep->v3warn(E_UNSUPPORTED,
     733                 :             :                               "Unsupported: Write to variable in sampled expression");
     734                 :             :             } else {
     735                 :             :                 VNRelinker relinkHandle;
     736                 :             :                 nodep->unlinkFrBack(&relinkHandle);
     737                 :             :                 AstSampled* const newp = newSampledExpr(nodep);
     738                 :             :                 relinkHandle.relink(newp);
     739                 :             :                 newp->user1(1);
     740                 :             :                 v3Global.setHasSampled();
     741                 :             :             }
     742                 :             :         }
     743                 :             :     }
     744                 :             :     // Don't sample sensitivities
     745                 :             :     void visit(AstSenItem* nodep) override {
     746                 :             :         VL_RESTORER(m_inSampled);
     747                 :             :         m_inSampled = false;
     748                 :             :         iterateChildren(nodep);
     749                 :             :     }
     750                 :             :     void visit(AstPExprClause* nodep) override {
     751                 :             :         AstNode* stmtsp = nullptr;
     752                 :             :         if (nodep->pass() && m_passsp) {
     753                 :             :             // Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
     754                 :             :             stmtsp = m_passsp->cloneTree(true);
     755                 :             :         } else if (!nodep->pass() && m_failsp) {
     756                 :             :             stmtsp = m_failsp->cloneTree(true);
     757                 :             :         }
     758                 :             :         if (stmtsp) {
     759                 :             :             stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
     760                 :             :                 // References inside action blocks shouldn't be implicitly sampled
     761                 :             :                 // m_passsp/m_failsp have been already visited once and refs explicitly sampled
     762                 :             :                 // are handled already
     763                 :             :                 refp->user2(1);
     764                 :             :             });
     765                 :             :             nodep->replaceWith(stmtsp);
     766                 :             :         } else {
     767                 :             :             nodep->unlinkFrBack();
     768                 :             :         }
     769                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     770                 :             :     }
     771                 :             :     void visit(AstPExpr* nodep) override {
     772                 :             :         if (m_inRestrict) {
     773                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     774                 :             :         } else {
     775                 :             :             iterateChildren(nodep);
     776                 :             :         }
     777                 :             :     }
     778                 :             : 
     779                 :             :     //========== Statements
     780                 :             :     void visit(AstDisplay* nodep) override {
     781                 :             :         iterateChildren(nodep);
     782                 :             :         // Replace the special types with standard text
     783                 :             :         if (nodep->displayType() == VDisplayType::DT_INFO) {
     784                 :             :             replaceDisplay(nodep, "-Info");
     785                 :             :         } else if (nodep->displayType() == VDisplayType::DT_WARNING) {
     786                 :             :             replaceDisplay(nodep, "%%Warning");
     787                 :             :         } else if (nodep->displayType() == VDisplayType::DT_ERROR) {
     788                 :             :             replaceDisplay(nodep, "%%Error");
     789                 :             :         } else if (nodep->displayType() == VDisplayType::DT_FATAL) {
     790                 :             :             replaceDisplay(nodep, "%%Fatal");
     791                 :             :         } else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
     792                 :             :             nodep->displayType(VDisplayType::DT_DISPLAY);
     793                 :             :             FileLine* const fl = nodep->fileline();
     794                 :             : 
     795                 :             :             AstSenItem* monSenItemsp = nullptr;
     796                 :             :             if (AstNode* const monExprsp = nodep->fmtp()->exprsp()) {
     797                 :             :                 monExprsp->foreachAndNext([&](AstVarRef* varrefp) {
     798                 :             :                     AstSenItem* const senItemp
     799                 :             :                         = new AstSenItem{fl, VEdgeType::ET_CHANGED,
     800                 :             :                                          // Clone so get VarRef or VarXRef as needed
     801                 :             :                                          varrefp->cloneTree(false)};
     802                 :             :                     if (!monSenItemsp) {
     803                 :             :                         monSenItemsp = senItemp;
     804                 :             :                     } else {
     805                 :             :                         monSenItemsp->addNext(senItemp);
     806                 :             :                     }
     807                 :             :                 });
     808                 :             :             }
     809                 :             : 
     810                 :             :             AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
     811                 :             :             const auto monNum = ++m_monitorNum;
     812                 :             :             // Where $monitor was we do "__VmonitorNum = N;"
     813                 :             :             AstAssign* const newsetp = new AstAssign{
     814                 :             :                 fl, newMonitorNumVarRefp(nodep, VAccess::WRITE), new AstConst{fl, monNum}};
     815                 :             :             nodep->replaceWith(newsetp);
     816                 :             :             // Add "always_comb if (__VmonitorOn && __VmonitorNum==N) $display(...);"
     817                 :             :             AstNode* const stmtsp = nodep;
     818                 :             :             AstIf* const ifp = new AstIf{
     819                 :             :                 fl,
     820                 :             :                 new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
     821                 :             :                               new AstEq{fl, new AstConst{fl, monNum},
     822                 :             :                                         newMonitorNumVarRefp(nodep, VAccess::READ)}},
     823                 :             :                 stmtsp};
     824                 :             :             ifp->isBoundsCheck(true);  // To avoid LATCH warning
     825                 :             :             ifp->branchPred(VBranchPred::BP_UNLIKELY);
     826                 :             :             AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, monSenTree, ifp};
     827                 :             :             m_modp->addStmtsp(newp);
     828                 :             :         } else if (nodep->displayType() == VDisplayType::DT_STROBE) {
     829                 :             :             nodep->displayType(VDisplayType::DT_DISPLAY);
     830                 :             :             // Need one-shot
     831                 :             :             FileLine* const fl = nodep->fileline();
     832                 :             :             AstVar* const varp
     833                 :             :                 = new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
     834                 :             :                              nodep->findBitDType()};
     835                 :             :             m_modp->addStmtsp(varp);
     836                 :             :             // Where $strobe was we do "__Vstrobe = '1;"
     837                 :             :             AstAssign* const newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
     838                 :             :                                                      new AstConst{fl, AstConst::BitTrue{}}};
     839                 :             :             nodep->replaceWith(newsetp);
     840                 :             :             // Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
     841                 :             :             AstNode* const stmtsp = nodep;
     842                 :             :             AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
     843                 :             :             ifp->isBoundsCheck(true);  // To avoid LATCH warning
     844                 :             :             ifp->branchPred(VBranchPred::BP_UNLIKELY);
     845                 :             :             AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
     846                 :             :             stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
     847                 :             :                                           new AstConst{fl, AstConst::BitFalse{}}});
     848                 :             :             m_modp->addStmtsp(newp);
     849                 :             :         }
     850                 :             :     }
     851                 :             :     void visit(AstMonitorOff* nodep) override {
     852                 :             :         AstAssign* const newp
     853                 :             :             = new AstAssign{nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
     854                 :             :                             new AstConst{nodep->fileline(), AstConst::BitTrue{}, nodep->off()}};
     855                 :             :         nodep->replaceWith(newp);
     856                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     857                 :             :     }
     858                 :             :     void visit(AstAssert* nodep) override {  //
     859                 :             :         visitAssertionIterate(nodep, nodep->failsp());
     860                 :             :     }
     861                 :             :     void visit(AstAssertCtl* nodep) override {
     862                 :             :         if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
     863                 :             :             nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
     864                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     865                 :             :             return;
     866                 :             :         }
     867                 :             : 
     868                 :             :         iterateChildren(nodep);
     869                 :             : 
     870                 :             :         if (!resolveAssertType(nodep)) {
     871                 :             :             nodep->v3warn(E_UNSUPPORTED,
     872                 :             :                           "Unsupported: non-constant assert assertion-type expression");
     873                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     874                 :             :             return;
     875                 :             :         }
     876                 :             :         if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
     877                 :             :             && nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
     878                 :             :                                                    | VAssertType::UNIQUE0
     879                 :             :                                                    | VAssertType::PRIORITY)) {
     880                 :             :             nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
     881                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     882                 :             :             return;
     883                 :             :         }
     884                 :             :         if (!resolveControlType(nodep)) {
     885                 :             :             nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
     886                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     887                 :             :             return;
     888                 :             :         }
     889                 :             :         if (!resolveDirectiveType(nodep)) {
     890                 :             :             nodep->v3warn(E_UNSUPPORTED,
     891                 :             :                           "Unsupported: non-const assert directive type expression");
     892                 :             :             VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     893                 :             :             return;
     894                 :             :         }
     895                 :             : 
     896                 :             :         FileLine* const fl = nodep->fileline();
     897                 :             :         switch (nodep->ctlType()) {
     898                 :             :         case VAssertCtlType::ON:
     899                 :             :             UINFO(9, "Generating assertctl for a module: " << m_modp);
     900                 :             :             nodep->replaceWith(
     901                 :             :                 new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnSet("s
     902                 :             :                                      + std::to_string(nodep->ctlAssertTypes()) + ", "s
     903                 :             :                                      + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
     904                 :             :             break;
     905                 :             :         case VAssertCtlType::OFF:
     906                 :             :         case VAssertCtlType::KILL: {
     907                 :             :             UINFO(9, "Generating assertctl for a module: " << m_modp);
     908                 :             :             nodep->replaceWith(
     909                 :             :                 new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnClear("s
     910                 :             :                                      + std::to_string(nodep->ctlAssertTypes()) + " ,"s
     911                 :             :                                      + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
     912                 :             :             break;
     913                 :             :         }
     914                 :             :         case VAssertCtlType::LOCK:
     915                 :             :         case VAssertCtlType::UNLOCK:
     916                 :             :         case VAssertCtlType::PASS_ON:
     917                 :             :         case VAssertCtlType::PASS_OFF:
     918                 :             :         case VAssertCtlType::FAIL_ON:
     919                 :             :         case VAssertCtlType::FAIL_OFF:
     920                 :             :         case VAssertCtlType::NONVACUOUS_ON:
     921                 :             :         case VAssertCtlType::VACUOUS_OFF: {
     922                 :             :             nodep->unlinkFrBack();
     923                 :             :             nodep->v3warn(E_UNSUPPORTED, "Unsupported: $assertcontrol control_type '" << cvtToStr(
     924                 :             :                                              static_cast<int>(nodep->ctlType())) << "'");
     925                 :             :             break;
     926                 :             :         }
     927                 :             :         default: {
     928                 :             :             nodep->unlinkFrBack();
     929                 :             :             nodep->v3warn(EC_ERROR, "Bad $assertcontrol control_type '"
     930                 :             :                                         << cvtToStr(static_cast<int>(nodep->ctlType()))
     931                 :             :                                         << "' (IEEE 1800-2023 Table 20-5)");
     932                 :             :         }
     933                 :             :         }
     934                 :             :         VL_DO_DANGLING(pushDeletep(nodep), nodep);
     935                 :             :     }
     936                 :             :     void visit(AstAssertIntrinsic* nodep) override {  //
     937                 :             :         visitAssertionIterate(nodep, nodep->failsp());
     938                 :             :     }
     939                 :             :     void visit(AstCover* nodep) override {  //
     940                 :             :         visitAssertionIterate(nodep, nullptr);
     941                 :             :     }
     942                 :             :     void visit(AstRestrict* nodep) override {
     943                 :             :         VL_RESTORER(m_inRestrict);
     944                 :             :         m_inRestrict = true;
     945                 :             :         iterateChildren(nodep);
     946                 :             :         // IEEE says simulator ignores these
     947                 :             :         VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
     948                 :             :     }
     949                 :             : 
     950                 :             :     void visit(AstNodeModule* nodep) override {
     951                 :             :         VL_RESTORER(m_modp);
     952                 :             :         VL_RESTORER(m_modPastNum);
     953                 :             :         VL_RESTORER(m_modStrobeNum);
     954                 :             :         VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
     955                 :             :         m_modp = nodep;
     956                 :             :         m_modPastNum = 0;
     957                 :             :         m_modStrobeNum = 0;
     958                 :             :         m_modExpr2Sen2DelayedAlwaysp.clear();
     959                 :             :         iterateChildren(nodep);
     960                 :             :     }
     961                 :             :     void visit(AstNodeProcedure* nodep) override {
     962                 :             :         VL_RESTORER(m_procedurep);
     963                 :             :         m_procedurep = nodep;
     964                 :             :         iterateChildren(nodep);
     965                 :             :     }
     966                 :             :     void visit(AstGenBlock* nodep) override {
     967                 :             :         // This code is needed rather than a visitor in V3Begin,
     968                 :             :         // because V3Assert is called before V3Begin
     969                 :             :         VL_RESTORER(m_beginp);
     970                 :             :         m_beginp = nodep;
     971                 :             :         iterateChildren(nodep);
     972                 :             :     }
     973                 :             :     void visit(AstBegin* nodep) override {
     974                 :             :         // This code is needed rather than a visitor in V3Begin,
     975                 :             :         // because V3Assert is called before V3Begin
     976                 :             :         VL_RESTORER(m_beginp);
     977                 :             :         m_beginp = nodep;
     978                 :             :         iterateChildren(nodep);
     979                 :             :     }
     980                 :             : 
     981                 :             :     void visit(AstNode* nodep) override { iterateChildren(nodep); }
     982                 :             : 
     983                 :             : public:
     984                 :             :     // CONSTRUCTORS
     985                 :             :     explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
     986                 :             :     ~AssertVisitor() override {
     987                 :             :         V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
     988                 :             :         V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
     989                 :             :         V3Stats::addStat("Assertions, cover statements", m_statCover);
     990                 :             :         V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
     991                 :             :         V3Stats::addStat("Assertions, $past variables", m_statPastVars);
     992                 :             :     }
     993                 :             : };
     994                 :             : 
     995                 :             : //######################################################################
     996                 :             : // Top Assert class
     997                 :             : 
     998                 :             : void V3Assert::assertAll(AstNetlist* nodep) {
     999                 :             :     UINFO(2, __FUNCTION__ << ":");
    1000                 :             :     { AssertVisitor{nodep}; }  // Destruct before checking
    1001                 :             :     V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeEitherLevel() >= 3);
    1002                 :             : }
        

Generated by: LCOV version 2.0-1