From 9abab2c36668d363e7756a6f91325b97ea3d074b Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 23 Apr 2022 14:44:48 +0100 Subject: [PATCH] Add separate AstInitialStatic node for static initializers Static variable initializers run before initial blocks, so use an explicitly different procedure type for them. This also enables us to now raise errors for assignments to const variables in initial blocks. --- Changes | 1 + src/V3Active.cpp | 8 ++++++++ src/V3AstNodes.h | 11 ++++++++++- src/V3Cdc.cpp | 1 + src/V3Class.cpp | 7 ++++++- src/V3EmitV.cpp | 1 + src/V3LinkParse.cpp | 2 +- src/V3Order.cpp | 3 +++ src/V3Width.cpp | 3 ++- 9 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Changes b/Changes index 016f91f50..acac7dee0 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 4.221 devel * Split --prof-threads into --prof-exec and --prof-pgo (#3365). [Geza Lore, Shunyao CAD] * Deprecate 'vluint64_t' and similar types (#3255). +* Raise error on assignment to const in initial blocks. [Geza Lore, Shunyao CAD] * Fix MSVC localtime_s (#3124). * Fix Bison 3.8.2 error (#3366). [elike-ypq] * Fix rare bug in -Oz (V3Localize) (#3286). [Geza Lore, Shunyao CAD] diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 043f05233..a0ed963ef 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -422,6 +422,14 @@ private: virtual void visit(AstActive* nodep) override { // Actives are being formed, so we can ignore any already made } + virtual void visit(AstInitialStatic* nodep) override { + // Relink to IACTIVE, unless already under it + UINFO(4, " INITIAL " << nodep << endl); + const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL}; + AstActive* const wantactivep = m_namer.getIActive(nodep->fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); + } virtual void visit(AstInitial* nodep) override { // Relink to IACTIVE, unless already under it UINFO(4, " INITIAL " << nodep << endl); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 1c86aad29..5082ae08d 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3407,7 +3407,7 @@ public: }; class AstInitialAutomatic final : public AstNodeProcedure { - // initial for automatic variables + // Automatic variable initialization // That is, it runs every function start, or class construction public: AstInitialAutomatic(FileLine* fl, AstNode* bodysp) @@ -3415,6 +3415,15 @@ public: ASTNODE_NODE_FUNCS(InitialAutomatic) }; +class AstInitialStatic final : public AstNodeProcedure { + // Static variable initialization + // That is, it runs at the beginning of simulation, before 'initial' blocks +public: + AstInitialStatic(FileLine* fl, AstNode* bodysp) + : ASTGEN_SUPER_InitialStatic(fl, bodysp) {} + ASTNODE_NODE_FUNCS(InitialStatic) +}; + class AstAlways final : public AstNodeProcedure { const VAlwaysKwd m_keyword; diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 871a5fd8c..acb305ab0 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -716,6 +716,7 @@ private: // Ignores virtual void visit(AstInitial*) override {} virtual void visit(AstInitialAutomatic*) override {} + virtual void visit(AstInitialStatic*) override {} virtual void visit(AstTraceDecl*) override {} virtual void visit(AstCoverToggle*) override {} virtual void visit(AstNodeDType*) override {} diff --git a/src/V3Class.cpp b/src/V3Class.cpp index c7bbc91c3..6a7a3ce4f 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -153,6 +153,11 @@ private: iterateChildren(nodep); if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); } } + virtual void visit(AstInitialStatic* nodep) override { + // But not AstInitialAutomatic, which remains under the class + iterateChildren(nodep); + if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); } + } virtual void visit(AstNodeMath* nodep) override {} // Short circuit virtual void visit(AstNodeStmt* nodep) override {} // Short circuit @@ -173,7 +178,7 @@ public: vscp->scopep(scopep); vscp->unlinkFrBack(); scopep->addVarp(vscp); - } else if (VN_IS(nodep, Initial)) { + } else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) { nodep->unlinkFrBack(); scopep->addActivep(nodep); } else { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index e04c15e4d..594795fa5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -99,6 +99,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { putqs(nodep, "end\n"); } virtual void visit(AstInitialAutomatic* nodep) override { iterateChildrenConst(nodep); } + virtual void visit(AstInitialStatic* nodep) override { iterateChildrenConst(nodep); } virtual void visit(AstAlways* nodep) override { putfs(nodep, "always "); if (m_sensesp) { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index b99b8e3a9..df1e36436 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -275,7 +275,7 @@ private: if (nodep->lifetime().isAutomatic()) { nodep->addNextHere(new AstInitialAutomatic{newfl, assp}); } else { - nodep->addNextHere(new AstInitial{newfl, assp}); + nodep->addNextHere(new AstInitialStatic{newfl, assp}); } } // 4. Under blocks, it's an initial value to be under an assign else { diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 090e81b7d..9007ce8f2 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -662,6 +662,9 @@ class OrderBuildVisitor final : public VNVisitor { virtual void visit(AstInitialAutomatic* nodep) override { // iterateLogic(nodep); } + virtual void visit(AstInitialStatic* nodep) override { // + iterateLogic(nodep); + } virtual void visit(AstAlways* nodep) override { // iterateLogic(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f4c350d6e..45a78ac8c 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2041,7 +2041,8 @@ private: if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) { nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ()); } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly - && (!m_ftaskp || !m_ftaskp->isConstructor()) && !VN_IS(m_procedurep, Initial)) { + && (!m_ftaskp || !m_ftaskp->isConstructor()) + && !VN_IS(m_procedurep, InitialStatic)) { // Too loose, but need to allow our generated first assignment // Move this to a property of the AstInitial block nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());