From 611ffbe04e9176c585153f5b97a4fb8cb3aa97c2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 2 Nov 2025 11:18:20 -0500 Subject: [PATCH 001/246] devel release --- CMakeLists.txt | 2 +- Changes | 8 ++++++++ configure.ac | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d311d6a7e..cefb24ae0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project( Verilator - VERSION 5.042 + VERSION 5.043 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index dc0a43231..f9b6edbe9 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,14 @@ The changes in each Verilator version are described below. The contributors that suggested or implemented a given issue are shown in []. Thanks! +Verilator 5.043 devel +========================== + +**Other:** + +* TBD + + Verilator 5.042 2025-11-02 ========================== diff --git a/configure.ac b/configure.ac index 3cd840db7..c67c7fdc7 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.042 2025-11-02], +AC_INIT([Verilator],[5.043 devel], [https://verilator.org], [verilator],[https://verilator.org]) From d066504bb927824397813a5cda7814cf3774383f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 2 Nov 2025 22:11:02 +0100 Subject: [PATCH 002/246] Optimize away calls to empty functions (#6626) --- src/V3AstNodeOther.h | 5 ++++- src/V3Const.cpp | 17 ++++++++++++++++- test_regress/t/t_timing_debug2.out | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index a335e579a..0ed7cf774 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -598,7 +598,10 @@ public: bool recursive() const { return m_recursive; } void cost(int cost) { m_cost = cost; } // Special methods - bool emptyBody() const { return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp(); } + bool emptyBody() const { + return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual() + && !dpiImportPrototype(); + } }; class AstCLocalScope final : public AstNode { // Pack statements into an unnamed scope when generating C++ diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 073997556..a1dd854b9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3669,10 +3669,25 @@ class ConstVisitor final : public VNVisitor { void visit(AstStmtExpr* nodep) override { iterateChildren(nodep); - if (!nodep->exprp() || VN_IS(nodep->exprp(), Const)) { + // Malformed due to child being deleted, remove here + if (!nodep->exprp()) { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); return; } + // Remove if expression is trivially pure + if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), VarRef)) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + // Remove calls to empty functions + if (AstCCall* const callp = VN_CAST(nodep->exprp(), CCall)) { + AstCFunc* const funcp = callp->funcp(); + if (!callp->argsp() && funcp->emptyBody()) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + } + // TODO if there's an ExprStmt underneath just keep lower statements // (No current test case needs this) // TODO if non-pure, can remove. First need to clean up that many expressions used diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index 3a07d9ac8..600ec9aa6 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -205,6 +205,7 @@ -V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:173 +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:247 -V{t#,#} Process forked at t/t_timing_class.v:246 finished @@ -568,6 +569,7 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:257 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:174 +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip @@ -1202,7 +1204,9 @@ -V{t#,#} Process forked at t/t_timing_class.v:250 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:245 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:175 +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay +-V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 @@ -1543,3 +1547,17 @@ -V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act -V{t#,#}End-of-eval cleanup -V{t#,#}+ Vt_timing_debug2___024root___eval_final +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ +-V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ +-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ From d3ca79368c7e55a9f7af771878e1df0a1a7f9fb4 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 3 Nov 2025 07:32:03 +0100 Subject: [PATCH 003/246] Internals: Replace AstMTaskBody with AstCFunc(#6280) (#6628) AstMTaskBody is somewhat redundant and is problematic for #6280. We used to wrap all MTasks in a CFunc before emit anyway. Now we create that CFunc when we create the ExecMTask in V3OrderParallel, and subsequently use the CFunc to represent the contents of the MTask. Final output and optimizations are the same, but internals are simplified to move towards #6280. No functional change. --- include/verilated_profiler.h | 2 +- src/V3AstNodeOther.h | 26 ----- src/V3AstNodeStmt.h | 24 ++-- src/V3AstNodes.cpp | 21 ++-- src/V3Depth.cpp | 17 --- src/V3EmitCFunc.h | 6 - src/V3ExecGraph.cpp | 214 +++++++++++++++++------------------ src/V3ExecGraph.h | 14 ++- src/V3Hasher.cpp | 3 - src/V3LifePost.cpp | 2 +- src/V3OrderParallel.cpp | 30 ++--- src/V3VariableOrder.cpp | 2 +- 12 files changed, 156 insertions(+), 205 deletions(-) diff --git a/include/verilated_profiler.h b/include/verilated_profiler.h index 2cabb39ff..1d8a2c5d4 100644 --- a/include/verilated_profiler.h +++ b/include/verilated_profiler.h @@ -120,7 +120,7 @@ public: m_type = Type::SECTION_PUSH; } void sectionPop() { m_type = Type::SECTION_POP; } - void mtaskBegin(uint32_t id, uint32_t predictStart, const char* hierBlock = "") { + void mtaskBegin(uint32_t id, uint32_t predictStart, const char* hierBlock) { m_payload.mtaskBegin.m_id = id; m_payload.mtaskBegin.m_predictStart = predictStart; m_payload.mtaskBegin.m_cpu = VlOs::getcpu(); diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 0ed7cf774..be568f2d6 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1100,32 +1100,6 @@ public: string name() const override VL_MT_STABLE { return m_name; } ASTGEN_MEMBERS_AstIntfRef; }; -class AstMTaskBody final : public AstNode { - // Hold statements for each MTask - // @astgen op1 := stmtsp : List[AstNode] - ExecMTask* m_execMTaskp = nullptr; - -public: - explicit AstMTaskBody(FileLine* fl) - : ASTGEN_SUPER_MTaskBody(fl) {} - ASTGEN_MEMBERS_AstMTaskBody; - void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } - const char* broken() const override { - BROKEN_RTN(!m_execMTaskp); - return nullptr; - } - void addStmtsFirstp(AstNode* nodep) { - if (stmtsp()) { - stmtsp()->addHereThisAsNext(nodep); - } else { - addStmtsp(nodep); - } - } - ExecMTask* execMTaskp() const { return m_execMTaskp; } - void execMTaskp(ExecMTask* execMTaskp) { m_execMTaskp = execMTaskp; } - void dump(std::ostream& str = std::cout) const override; - void dumpJson(std::ostream& str = std::cout) const override; -}; class AstModport final : public AstNode { // A modport in an interface // @astgen op1 := varsp : List[AstNode] diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 0825e6b27..1369bdefa 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -601,20 +601,21 @@ public: int instrCount() const override { return 0; } }; class AstExecGraph final : public AstNodeStmt { - // For parallel execution, this node contains a dependency graph. Each - // vertex in the graph is an ExecMTask, which contains a body for the - // mtask (an AstMTaskBody), which contains sequentially executed statements. - // - // The AstMTaskBody nodes are also children of this node, so we can visit - // them without traversing the graph. + // For parallel execution, this node contains a dependency graph. Each + // vertex in the graph is an ExecMTask, which includes a function that + // holds the sequential body of the mtask. // // The location where AstExecGraph appears as a procedural statement is // where the parallel graph will be executed. Execution proceeds after // the AstExecGraph when all threads have joined. // - // @astgen op1 := mTaskBodiesp : List[AstMTaskBody] - // In later phases, the statements that start the parallel execution - // @astgen op2 := stmtsp : List[AstNode] + // For code analysis purposes after scheduling, we keep a call to each + // MTask function as children of the AstExecGraph in 'stmtsp'. These + // are in a topological order so they represent a valid sequential + // execution of the graph. In `V3ExecGraph::implement`, we replace these + // statements with statements that dispatch to the thread pool for + // parallel execution. + // @astgen op1 := stmtsp : List[AstNode] V3Graph* const m_depGraphp; // contains ExecMTask vertices const string m_name; // Name of this AstExecGraph (for uniqueness at code generation) @@ -623,10 +624,7 @@ public: ~AstExecGraph() override; ASTGEN_MEMBERS_AstExecGraph; void cloneRelink() override { V3ERROR_NA; } // Not cloneable - const char* broken() const override { - BROKEN_RTN(!m_depGraphp); - return nullptr; - } + const char* broken() const override; string name() const override VL_MT_STABLE { return m_name; } V3Graph* depGraphp() { return m_depGraphp; } const V3Graph* depGraphp() const { return m_depGraphp; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c264c7ec9..778784ba0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -340,6 +340,16 @@ AstExecGraph::AstExecGraph(FileLine* fileline, const string& name) VL_MT_DISABLE AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); } +const char* AstExecGraph::broken() const { + BROKEN_RTN(!m_depGraphp); + for (const V3GraphVertex& vtx : m_depGraphp->vertices()) { + const ExecMTask* const mtaskp = vtx.as(); + AstCFunc* const funcp = mtaskp->funcp(); + BROKEN_RTN(!funcp || !funcp->brokeExists()); + } + return nullptr; +} + AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) { AstNodeExpr* const ap = new AstGte{fileline(), exprp, lhsp}; @@ -2538,17 +2548,6 @@ void AstSystemCSection::dumpJson(std::ostream& str) const { dumpJsonStr(str, "sectionType", sectionType().ascii()); dumpJsonGen(str); } -void AstMTaskBody::dump(std::ostream& str) const { - this->AstNode::dump(str); - str << " "; - m_execMTaskp->dump(str); -} -void AstMTaskBody::dumpJson(std::ostream& str) const { - str << ',' << '"' << "execMTask" << '"' << ':' << '"'; - m_execMTaskp->dump(str); // TODO: Consider dumping it as json object - str << '"'; - dumpJsonGen(str); -} void AstTypeTable::dump(std::ostream& str) const { this->AstNode::dump(str); for (int i = 0; i < static_cast(VBasicDTypeKwd::_ENUM_MAX); ++i) { diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index f1728812f..4c9b0082d 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -38,7 +38,6 @@ class DepthVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) AstCFunc* m_cfuncp = nullptr; // Current block - AstMTaskBody* m_mtaskbodyp = nullptr; // Current mtaskbody AstNode* m_stmtp = nullptr; // Current statement int m_depth = 0; // How deep in an expression int m_maxdepth = 0; // Maximum depth in an expression @@ -53,8 +52,6 @@ class DepthVisitor final : public VNVisitor { m_tempNames.get(nodep), nodep->dtypep()}; if (m_cfuncp) { m_cfuncp->addVarsp(varp); - } else if (m_mtaskbodyp) { - m_mtaskbodyp->addStmtsFirstp(varp); } else { nodep->v3fatalSrc("Deep expression not under a function"); } @@ -70,28 +67,14 @@ class DepthVisitor final : public VNVisitor { // VISITORS void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); - VL_RESTORER(m_mtaskbodyp); VL_RESTORER(m_depth); VL_RESTORER(m_maxdepth); m_cfuncp = nodep; - m_mtaskbodyp = nullptr; m_depth = 0; m_maxdepth = 0; m_tempNames.reset(); iterateChildren(nodep); } - void visit(AstMTaskBody* nodep) override { - VL_RESTORER(m_cfuncp); - VL_RESTORER(m_mtaskbodyp); - VL_RESTORER(m_depth); - VL_RESTORER(m_maxdepth); - m_cfuncp = nullptr; - m_mtaskbodyp = nodep; - m_depth = 0; - m_maxdepth = 0; - // We don't reset the names, as must share across tasks - iterateChildren(nodep); - } void visitStmt(AstNodeStmt* nodep) { VL_RESTORER(m_stmtp); VL_RESTORER(m_depth); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index b04556c38..f9763cfd2 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1623,11 +1623,6 @@ public: } // - void visit(AstMTaskBody* nodep) override { - VL_RESTORER(m_useSelfForThis); - m_useSelfForThis = true; - iterateChildrenConst(nodep); - } void visit(AstConsAssoc* nodep) override { putnbs(nodep, nodep->dtypep()->cType("", false, false)); puts("()"); @@ -1723,7 +1718,6 @@ public: void visit(AstExecGraph* nodep) override { // The location of the AstExecGraph within the containing AstCFunc is where we want to // invoke the graph and wait for it to complete. Emitting the children does just that. - UASSERT_OBJ(!nodep->mTaskBodiesp(), nodep, "These should have been lowered"); iterateChildrenConst(nodep); } diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index ca20bcd81..28f5b86db 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -33,16 +33,24 @@ VL_DEFINE_DEBUG_FUNCTIONS; -ExecMTask::ExecMTask(V3Graph* graphp, AstMTaskBody* bodyp) VL_MT_DISABLED // - : V3GraphVertex{graphp}, - m_bodyp{bodyp}, - m_id{s_nextId++}, - m_hashName{V3Hasher::uncachedHash(bodyp).toString()} { - UASSERT_OBJ(bodyp->stmtsp(), bodyp, "AstMTaskBody should already be populated for hashing"); - UASSERT_OBJ(!bodyp->execMTaskp(), bodyp, "AstMTaskBody already linked to an ExecMTask"); - bodyp->execMTaskp(this); +AstCFunc* ExecMTask::createCFunc(AstExecGraph* execGraphp, AstScope* scopep, AstNodeStmt* stmtsp, + uint32_t id) { + const std::string name = execGraphp->name() + "_mtask" + std::to_string(id); + AstCFunc* const funcp = new AstCFunc{execGraphp->fileline(), name, scopep}; + funcp->isLoose(true); + funcp->dontCombine(true); + funcp->addStmtsp(stmtsp); + if (scopep) scopep->addBlocksp(funcp); + return funcp; } +ExecMTask::ExecMTask(AstExecGraph* execGraphp, AstScope* scopep, + AstNodeStmt* stmtsp) VL_MT_DISABLED // + : V3GraphVertex{execGraphp->depGraphp()}, + m_id{s_nextId++}, + m_funcp{createCFunc(execGraphp, scopep, stmtsp, m_id)}, + m_hashName{V3Hasher::uncachedHash(m_funcp).toString()} {} + void ExecMTask::dump(std::ostream& str) const { str << name() << "." << cvtToHex(this); if (priority() || cost()) str << " [pr=" << priority() << " c=" << cvtToStr(cost()) << "]"; @@ -538,37 +546,32 @@ public: selfTestNormalFirst(); } static void selfTestNormalFirst() { - V3Graph graph; FileLine* const flp = v3Global.rootp()->fileline(); - std::vector mTaskBodyps; - const auto makeBody = [&]() { - AstMTaskBody* const bodyp = new AstMTaskBody{flp}; - mTaskBodyps.push_back(bodyp); - bodyp->addStmtsp(new AstComment{flp, ""}); - return bodyp; - }; - ExecMTask* const t0 = new ExecMTask{&graph, makeBody()}; + AstExecGraph* const execGraphp = new AstExecGraph{flp, "test"}; + V3Graph& graph = *execGraphp->depGraphp(); + const auto makeBody = [&]() -> AstNodeStmt* { return new AstComment{flp, ""}; }; + ExecMTask* const t0 = new ExecMTask{execGraphp, nullptr, makeBody()}; t0->cost(1000); t0->priority(1100); - ExecMTask* const t1 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t1 = new ExecMTask{execGraphp, nullptr, makeBody()}; t1->cost(100); t1->priority(100); - ExecMTask* const t2 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t2 = new ExecMTask{execGraphp, nullptr, makeBody()}; t2->cost(100); t2->priority(100); t2->threads(2); - ExecMTask* const t3 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t3 = new ExecMTask{execGraphp, nullptr, makeBody()}; t3->cost(100); t3->priority(100); t3->threads(3); - ExecMTask* const t4 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t4 = new ExecMTask{execGraphp, nullptr, makeBody()}; t4->cost(100); t4->priority(100); t4->threads(3); - ExecMTask* const t5 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t5 = new ExecMTask{execGraphp, nullptr, makeBody()}; t5->cost(100); t5->priority(100); - ExecMTask* const t6 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t6 = new ExecMTask{execGraphp, nullptr, makeBody()}; t6->cost(100); t6->priority(100); @@ -666,24 +669,20 @@ public: UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 4), 1360); UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 5), 1360); - for (AstNode* const nodep : mTaskBodyps) nodep->deleteTree(); + for (V3GraphVertex& vtx : graph.vertices()) vtx.as()->funcp()->deleteTree(); + VL_DO_DANGLING(execGraphp->deleteTree(), execGraphp); ThreadSchedule::s_mtaskState.clear(); } static void selfTestHierFirst() { - V3Graph graph; FileLine* const flp = v3Global.rootp()->fileline(); - std::vector mTaskBodyps; - const auto makeBody = [&]() { - AstMTaskBody* const bodyp = new AstMTaskBody{flp}; - mTaskBodyps.push_back(bodyp); - bodyp->addStmtsp(new AstComment{flp, ""}); - return bodyp; - }; - ExecMTask* const t0 = new ExecMTask{&graph, makeBody()}; + AstExecGraph* const execGraphp = new AstExecGraph{flp, "test"}; + V3Graph& graph = *execGraphp->depGraphp(); + const auto makeBody = [&]() -> AstNodeStmt* { return new AstComment{flp, ""}; }; + ExecMTask* const t0 = new ExecMTask{execGraphp, nullptr, makeBody()}; t0->cost(1000); t0->priority(1100); t0->threads(2); - ExecMTask* const t1 = new ExecMTask{&graph, makeBody()}; + ExecMTask* const t1 = new ExecMTask{execGraphp, nullptr, makeBody()}; t1->cost(100); t1->priority(100); @@ -725,7 +724,8 @@ public: UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t1, 0), 1100); UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t1, 1), 1130); - for (AstNode* const nodep : mTaskBodyps) nodep->deleteTree(); + for (V3GraphVertex& vtx : graph.vertices()) vtx.as()->funcp()->deleteTree(); + VL_DO_DANGLING(execGraphp->deleteTree(), execGraphp); ThreadSchedule::s_mtaskState.clear(); } @@ -790,6 +790,24 @@ void normalizeCosts(Costs& costs) { } } +void removeEmptyMTasks(V3Graph* execMTaskGraphp) { + for (V3GraphVertex* const vtxp : execMTaskGraphp->vertices().unlinkable()) { + ExecMTask* const mtaskp = vtxp->as(); + AstCFunc* const funcp = mtaskp->funcp(); + if (funcp->stmtsp()) continue; + + UINFO(6, "Removing empty MTask " << mtaskp->name()); + // Redirect edges + mtaskp->rerouteEdges(execMTaskGraphp); + // Delete the MTask function + VL_DO_DANGLING(funcp->unlinkFrBack()->deleteTree(), funcp); + // Delete the MTask vertex + VL_DO_DANGLING(mtaskp->unlinkDelete(execMTaskGraphp), mtaskp); + } + // Remove redundant dependencies + execMTaskGraphp->removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); +} + void fillinCosts(V3Graph* execMTaskGraphp) { // Pass 1: See what profiling data applies Costs costs; // For each mtask, costs @@ -797,7 +815,7 @@ void fillinCosts(V3Graph* execMTaskGraphp) { for (V3GraphVertex& vtx : execMTaskGraphp->vertices()) { ExecMTask* const mtp = vtx.as(); // This estimate is 64 bits, but the final mtask graph algorithm needs 32 bits - const uint64_t costEstimate = V3InstrCount::count(mtp->bodyp(), false); + const uint64_t costEstimate = V3InstrCount::count(mtp->funcp(), false); const uint64_t costProfiled = V3Control::getProfileData(v3Global.opt.prefix(), mtp->hashName()); if (costProfiled) { @@ -857,30 +875,6 @@ void finalizeCosts(V3Graph* execMTaskGraphp) { } } - // Some MTasks may now have zero cost, eliminate those. - // (It's common for tasks to shrink to nothing when V3LifePost - // removes dly assignments.) - for (V3GraphVertex* const vtxp : execMTaskGraphp->vertices().unlinkable()) { - ExecMTask* const mtp = vtxp->as(); - - // Don't rely on checking mtp->cost() == 0 to detect an empty task. - // Our cost-estimating logic is just an estimate. Instead, check - // the MTaskBody to see if it's empty. That's the source of truth. - AstMTaskBody* const bodyp = mtp->bodyp(); - if (!bodyp->stmtsp()) { // Kill this empty mtask - UINFO(6, "Removing zero-cost " << mtp->name()); - for (V3GraphEdge& in : mtp->inEdges()) { - for (V3GraphEdge& out : mtp->outEdges()) { - new V3GraphEdge{execMTaskGraphp, in.fromp(), out.top(), 1}; - } - } - VL_DO_DANGLING(mtp->unlinkDelete(execMTaskGraphp), mtp); - // Also remove and delete the AstMTaskBody, otherwise it would - // keep a dangling pointer to the ExecMTask. - VL_DO_DANGLING(bodyp->unlinkFrBack()->deleteTree(), bodyp); - } - } - // Removing tasks may cause edges that were formerly non-transitive to // become transitive. Also we just created new edges around the removed // tasks, which could be transitive. Prune out all transitive edges. @@ -907,6 +901,7 @@ void finalizeCosts(V3Graph* execMTaskGraphp) { void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId, AstCFunc* funcp, const ExecMTask* mtaskp) { + AstScope* const scopep = v3Global.rootp()->topScopep()->scopep(); AstNodeModule* const modp = v3Global.rootp()->topModulep(); FileLine* const fl = modp->fileline(); @@ -940,8 +935,11 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId, addCStmt("vlSymsp->_vm_pgoProfiler.startCounter(" + std::to_string(mtaskp->id()) + ");"); } - // Move the actual body into this function - funcp->addStmtsp(mtaskp->bodyp()->unlinkFrBack()); + // Call the MTask function + AstCCall* const callp = new AstCCall{fl, mtaskp->funcp()}; + callp->selfPointer(VSelfPointerText{VSelfPointerText::VlSyms{}, scopep->nameDotless()}); + callp->dtypeSetVoid(); + funcp->addStmtsp(callp->makeStmt()); if (v3Global.opt.profPgo()) { // No lock around stopCounter, as counter numbers are unique per thread @@ -1093,56 +1091,38 @@ void addThreadStartToExecGraph(AstExecGraph* const execGraphp, } } -void wrapMTaskBodies(AstExecGraph* const execGraphp) { - FileLine* const flp = execGraphp->fileline(); - const string& tag = execGraphp->name(); - AstNodeModule* const modp = v3Global.rootp()->topModulep(); - - for (AstMTaskBody* mtaskBodyp = execGraphp->mTaskBodiesp(); mtaskBodyp; - mtaskBodyp = VN_AS(mtaskBodyp->nextp(), MTaskBody)) { - ExecMTask* const mtaskp = mtaskBodyp->execMTaskp(); - const std::string name = tag + "_mtask" + std::to_string(mtaskp->id()); - AstCFunc* const funcp = new AstCFunc{flp, name, nullptr}; - funcp->isLoose(true); - modp->addStmtsp(funcp); +void processMTaskBodies(AstExecGraph* const execGraphp) { + for (V3GraphVertex* const vtxp : execGraphp->depGraphp()->vertices().unlinkable()) { + ExecMTask* const mtaskp = vtxp->as(); + AstCFunc* const funcp = mtaskp->funcp(); + // Temporarily unlink function body so we can add more statemetns + AstNode* stmtsp = funcp->stmtsp()->unlinkFrBackWithNext(); // Helper function to make the code a bit more legible const auto addCStmt = [=](const string& stmt) -> void { // - funcp->addStmtsp(new AstCStmt{flp, stmt}); + funcp->addStmtsp(new AstCStmt{execGraphp->fileline(), stmt}); }; - addCStmt("static constexpr unsigned taskId = " + cvtToStr(mtaskp->id()) + ";"); - + // Profiling mtaskStart if (v3Global.opt.profExec()) { - const string& predictStart = std::to_string(mtaskp->predictStart()); - if (v3Global.opt.hierChild()) { - addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart - + ", \"" + v3Global.opt.topModule() + "\");"); - } else { - addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(taskId, " + predictStart - + ");"); - } + std::string args = std::to_string(mtaskp->id()); + args += ", " + std::to_string(mtaskp->predictStart()); + args += ", \""; + if (v3Global.opt.hierChild()) args += v3Global.opt.topModule(); + args += "\""; + addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskBegin(" + args + ");"); } - // Set mtask ID in the run-time system - addCStmt("Verilated::mtaskId(taskId);"); - - // Run body - funcp->addStmtsp(mtaskBodyp->stmtsp()->unlinkFrBackWithNext()); - + addCStmt("Verilated::mtaskId(" + std::to_string(mtaskp->id()) + ");"); + // Add back the body + funcp->addStmtsp(stmtsp); // Flush message queue addCStmt("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);"); - + // Profiling mtaskEnd if (v3Global.opt.profExec()) { - const string& predictCost = std::to_string(mtaskp->cost()); - addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskEnd(" + predictCost + ");"); + const std::string& args = std::to_string(mtaskp->cost()); + addCStmt("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).mtaskEnd(" + args + ");"); } - - // AstMTask will simply contain a call - AstCCall* const callp = new AstCCall{flp, funcp}; - callp->selfPointer(VSelfPointerText{VSelfPointerText::This{}}); - callp->dtypeSetVoid(); - mtaskBodyp->addStmtsp(callp->makeStmt()); } } @@ -1150,8 +1130,7 @@ void implementExecGraph(AstExecGraph* const execGraphp, const ThreadSchedule& sc // Nothing to be done if there are no MTasks in the graph at all. if (execGraphp->depGraphp()->empty()) return; - // Create a function to be run by each thread. Note this moves all AstMTaskBody nodes form the - // AstExecGraph into the AstCFunc created + // Create a function to be run by each thread. const std::vector& funcps = createThreadFunctions(schedule, execGraphp->name()); UASSERT(!funcps.empty(), "Non-empty ExecGraph yields no threads?"); @@ -1159,9 +1138,30 @@ void implementExecGraph(AstExecGraph* const execGraphp, const ThreadSchedule& sc addThreadStartToExecGraph(execGraphp, funcps, schedule.id()); } +// Called by Verilator top stage void implement(AstNetlist* netlistp) { - // Called by Verilator top stage - netlistp->topModulep()->foreach([&](AstExecGraph* execGraphp) { + // Gather all ExecGraphs + std::vector execGraphps; + netlistp->topModulep()->foreach([&](AstExecGraph* egp) { execGraphps.emplace_back(egp); }); + + // Process each + for (AstExecGraph* const execGraphp : execGraphps) { + // We can delete the placeholder calls to the MTask functions that + // were used for code analysis until now. We will replace them with + // statements that dispatch execution to the thread pool. + if (execGraphp->stmtsp()) execGraphp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); + + // Some MTasks may have become empty after scheduling due to + // optimizations after scheduling. Remove those. + removeEmptyMTasks(execGraphp->depGraphp()); + + // In some very small test cases, we might end up with a completely + // empty ExecGraph, if so just delete it. + if (execGraphp->depGraphp()->empty()) { + VL_DO_DANGLING(execGraphp->unlinkFrBack()->deleteTree(), execGraphp); + return; + } + // Back in V3Order, we partitioned mtasks using provisional cost // estimates. However, V3Order precedes some optimizations (notably // V3LifePost) that can change the cost of logic within each mtask. @@ -1180,8 +1180,8 @@ void implement(AstNetlist* netlistp) { V3Stats::addStatSum("Optimizations, Thread schedule count", static_cast(packed.size())); - // Wrap each MTask body into a CFunc for better profiling/debugging - wrapMTaskBodies(execGraphp); + // Process MTask function bodies to add additional code + processMTaskBodies(execGraphp); for (const ThreadSchedule& schedule : packed) { // Replace the graph body with its multi-threaded implementation. @@ -1189,7 +1189,7 @@ void implement(AstNetlist* netlistp) { } addThreadEndWrapper(execGraphp); - }); + } } void selfTest() { diff --git a/src/V3ExecGraph.h b/src/V3ExecGraph.h index d16941b5c..2c13b92f9 100644 --- a/src/V3ExecGraph.h +++ b/src/V3ExecGraph.h @@ -25,7 +25,10 @@ #include class AstNetlist; -class AstMTaskBody; +class AstCFunc; +class AstExecGraph; +class AstNodeStmt; +class AstScope; //************************************************************************* // MTasks and graph structures @@ -33,9 +36,9 @@ class AstMTaskBody; class ExecMTask final : public V3GraphVertex { VL_RTTI_IMPL(ExecMTask, V3GraphVertex) private: - AstMTaskBody* const m_bodyp; // Task body const uint32_t m_id; // Unique ID of this ExecMTask. static std::atomic s_nextId; // Next ID to use + AstCFunc* const m_funcp; // The function that contains the task body const std::string m_hashName; // Hashed name based on body for profile-driven optimization // Predicted critical path from the start of this mtask to the ends of the graph that are // reachable from this mtask. In abstract time units. @@ -46,9 +49,12 @@ private: int m_threads = 1; // Threads used by this mtask VL_UNCOPYABLE(ExecMTask); + static AstCFunc* createCFunc(AstExecGraph* execGraphp, AstScope* scopep, AstNodeStmt* stmtsp, + uint32_t id); + public: - ExecMTask(V3Graph* graphp, AstMTaskBody* bodyp) VL_MT_DISABLED; - AstMTaskBody* bodyp() const { return m_bodyp; } + ExecMTask(AstExecGraph* execGraphp, AstScope* scopep, AstNodeStmt* stmtsp) VL_MT_DISABLED; + AstCFunc* funcp() const { return m_funcp; } uint32_t id() const VL_MT_SAFE { return m_id; } uint32_t priority() const { return m_priority; } void priority(uint32_t pri) { m_priority = pri; } diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 14438c196..03e6261ca 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -513,9 +513,6 @@ class HasherVisitor final : public VNVisitorConst { iterateConstNull(nodep->ftaskp()); }); } - void visit(AstMTaskBody* nodep) override { - m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {}); - } void visit(AstNodeProcedure* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {}); } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 4703ea0f9..c39463967 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -290,7 +290,7 @@ class LifePostDlyVisitor final : public VNVisitorConst { const ExecMTask* const mtaskp = mtaskVtx.as(); VL_RESTORER(m_execMTaskp); m_execMTaskp = mtaskp; - iterateConst(mtaskp->bodyp()); + trace(mtaskp->funcp()); } } void visit(AstCFunc* nodep) override { diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index b84d8a3cd..d3b4b3904 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -1763,7 +1763,7 @@ class DpiThreadsVisitor final : public VNVisitorConst { public: // CONSTRUCTORS - explicit DpiThreadsVisitor(AstMTaskBody* nodep) { iterateConst(nodep); } + explicit DpiThreadsVisitor(AstCFunc* nodep) { iterateConst(nodep); } int threads() const { return m_threads; } ~DpiThreadsVisitor() override = default; @@ -2431,8 +2431,9 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, OrderMoveGraph& mov if (dumpGraphLevel() >= 9) moveGraph.dumpDotFilePrefixed(tag + "_ordermv_pruned"); // Create the AstExecGraph node which represents the execution of the MTask graph. - FileLine* const rootFlp = v3Global.rootp()->fileline(); - AstExecGraph* const execGraphp = new AstExecGraph{rootFlp, tag}; + FileLine* const flp = v3Global.rootp()->fileline(); + AstScope* const scopep = v3Global.rootp()->topScopep()->scopep(); + AstExecGraph* const execGraphp = new AstExecGraph{flp, tag}; V3Graph* const depGraphp = execGraphp->depGraphp(); // Translate the LogicMTask graph into the corresponding ExecMTask graph, @@ -2468,24 +2469,23 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, OrderMoveGraph& mov VL_DO_DANGLING(mVtxp->unlinkDelete(&moveGraph), mVtxp); } - // We have 2 objects, because AstMTaskBody is an AstNode, and ExecMTask is a GraphVertex. - // To combine them would involve multiple inheritance. - - // Construct the actual MTaskBody - AstMTaskBody* const bodyp = new AstMTaskBody{rootFlp}; - execGraphp->addMTaskBodiesp(bodyp); - bodyp->addStmtsp(emitter.getStmts()); - UASSERT_OBJ(bodyp->stmtsp(), bodyp, "Should not try to create empty MTask"); - // Create the ExecMTask - ExecMTask* const execMTaskp = new ExecMTask{depGraphp, bodyp}; - if (!v3Global.opt.hierBlocks().empty()) - execMTaskp->threads(DpiThreadsVisitor{bodyp}.threads()); + ExecMTask* const execMTaskp = new ExecMTask{execGraphp, scopep, emitter.getStmts()}; + if (!v3Global.opt.hierBlocks().empty()) { + execMTaskp->threads(DpiThreadsVisitor{execMTaskp->funcp()}.threads()); + } const bool newEntry = logicMTaskToExecMTask.emplace(mTaskp, execMTaskp).second; UASSERT_OBJ(newEntry, mTaskp, "LogicMTasks should be processed in dependencyorder"); UINFO(3, "Final '" << tag << "' LogicMTask " << mTaskp->id() << " maps to ExecMTask" << execMTaskp->id()); + // For code analysis purposes, we can pretend the AstExecGraph runs the + // MTasks sequentially, in some topological order that respects edges. + // The order they are created here happens to be just such an order. + AstCCall* const callp = new AstCCall{flp, execMTaskp->funcp()}; + callp->dtypeSetVoid(); + execGraphp->addStmtsp(callp->makeStmt()); + // Add the dependency edges between ExecMTasks for (const V3GraphEdge& edge : mTaskp->inEdges()) { const V3GraphVertex* fromVxp = edge.fromp(); diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index e241ae495..7560fc01d 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -53,7 +53,7 @@ class GatherMTaskAffinity final : VNVisitorConst { GatherMTaskAffinity(const ExecMTask* mTaskp, MTaskAffinityMap& results) : m_results{results} , m_id{mTaskp->id()} { - iterateChildrenConst(mTaskp->bodyp()); + iterateConst(mTaskp->funcp()); } ~GatherMTaskAffinity() = default; VL_UNMOVABLE(GatherMTaskAffinity); From faaa2db8446ef5fd4e0751d54db0155e2f1671f8 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 3 Nov 2025 12:29:39 +0000 Subject: [PATCH 004/246] Fix merging of impure assignments in gate optimization (#6629) (#6630) --- src/V3Gate.cpp | 21 +- test_regress/t/t_cover_line.out | 17 + test_regress/t/t_cover_line.v | 12 + test_regress/t/t_cover_line_cc.info.out | 127 +++-- test_regress/t/t_cover_line_trace.out | 725 +++++++++++++----------- 5 files changed, 493 insertions(+), 409 deletions(-) diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 4cf573f0a..f6e5a9198 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -1097,6 +1097,7 @@ public: class GateMergeAssignments final { GateGraph& m_graph; size_t m_statAssignMerged = 0; // Statistic tracking + std::vector m_toRemove; // Logic vertices to delete // assemble two Sel into one if possible AstSel* merge(AstSel* prevSelp, AstSel* currSelp) { @@ -1146,18 +1147,16 @@ class GateMergeAssignments final { // replace preSel with newSel prevSelp->replaceWith(newSelp); VL_DO_DANGLING(prevSelp->deleteTree(), prevSelp); - // create new rhs for pre assignment - AstNode* const newRhsp = new AstConcat{prevAssignp->rhsp()->fileline(), - prevAssignp->rhsp()->cloneTreePure(false), - assignp->rhsp()->cloneTreePure(false)}; - AstNode* const prevRhsp = prevAssignp->rhsp(); - prevRhsp->replaceWith(newRhsp); - VL_DO_DANGLING(prevRhsp->deleteTree(), prevRhsp); + // Update RHS of the prev assignment, reusing existing parts (might be impure). + prevAssignp->rhsp(new AstConcat{prevAssignp->rhsp()->fileline(), + prevAssignp->rhsp()->unlinkFrBack(), + assignp->rhsp()->unlinkFrBack()}); // Why do we care about the type of an assignment? prevAssignp->dtypeChgWidthSigned(prevAssignp->width() + assignp->width(), prevAssignp->width() + assignp->width(), VSigning::SIGNED); - // Don't need to delete assignp, will be handled + // We will delete the current assignment + m_toRemove.emplace_back(lVtxp); // Update the graph while (V3GraphEdge* const iedgep = lVtxp->inEdges().frontp()) { @@ -1182,6 +1181,12 @@ class GateMergeAssignments final { for (V3GraphVertex& vtx : graph.vertices()) { if (GateVarVertex* const vVtxp = vtx.cast()) process(vVtxp); } + // Delete merged assignments + for (GateLogicVertex* const lVtxp : m_toRemove) { + AstNode* const nodep = lVtxp->nodep(); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + VL_DO_DANGLING(lVtxp->unlinkDelete(&m_graph), lVtxp); + } } ~GateMergeAssignments() { diff --git a/test_regress/t/t_cover_line.out b/test_regress/t/t_cover_line.out index 79e9a6694..06d6e7515 100644 --- a/test_regress/t/t_cover_line.out +++ b/test_regress/t/t_cover_line.out @@ -443,6 +443,11 @@ -000001 point: comment=block hier=top.t.cond1 string s; + struct packed { + logic unsigned [15:0] a; + logic unsigned [15:0] b; + } pstruct; + 000021 function logic func_side_effect; +000021 point: comment=block hier=top.t.cond1 000021 $display("SIDE EFFECT"); @@ -535,5 +540,17 @@ 000011 else k = 0; +000011 point: comment=else hier=top.t.cond1 end + +~000010 assign pstruct.a = cyc == 1 ? 16'd2 : 16'd3; +-000001 point: comment=cond_then hier=top.t.cond1 ++000010 point: comment=cond_else hier=top.t.cond1 + assign pstruct.b = 16'd0; + + 000010 always @(posedge clk) begin ++000010 point: comment=block hier=top.t.cond1 +%000009 if (cyc == 2) $display("%08x", pstruct); +-000001 point: comment=if hier=top.t.cond1 +-000009 point: comment=else hier=top.t.cond1 + end endmodule diff --git a/test_regress/t/t_cover_line.v b/test_regress/t/t_cover_line.v index a6df141ce..8e8811068 100644 --- a/test_regress/t/t_cover_line.v +++ b/test_regress/t/t_cover_line.v @@ -315,6 +315,11 @@ module cond(input logic clk, input int cyc); Getter1 getter1 = new; string s; + struct packed { + logic unsigned [15:0] a; + logic unsigned [15:0] b; + } pstruct; + function logic func_side_effect; $display("SIDE EFFECT"); return 1; @@ -361,4 +366,11 @@ module cond(input logic clk, input int cyc); if (k ? 1 : 0) k = 1; else k = 0; end + + assign pstruct.a = cyc == 1 ? 16'd2 : 16'd3; + assign pstruct.b = 16'd0; + + always @(posedge clk) begin + if (cyc == 2) $display("%08x", pstruct); + end endmodule diff --git a/test_regress/t/t_cover_line_cc.info.out b/test_regress/t/t_cover_line_cc.info.out index ceb68f067..edae983eb 100644 --- a/test_regress/t/t_cover_line_cc.info.out +++ b/test_regress/t/t_cover_line_cc.info.out @@ -149,65 +149,72 @@ DA:304,1 DA:305,20 DA:306,20 DA:315,1 -DA:318,21 -DA:319,21 -DA:320,21 -DA:323,10 -DA:325,10 -DA:328,31 -BRDA:328,0,0,0 -BRDA:328,0,1,31 -DA:329,28 -BRDA:329,0,0,3 -BRDA:329,0,1,28 -DA:330,21 -BRDA:330,0,0,21 -BRDA:330,0,1,0 -DA:331,10 -DA:332,10 -BRDA:332,0,0,10 -BRDA:332,0,1,3 -BRDA:332,0,2,7 -DA:333,10 -BRDA:333,0,0,10 -BRDA:333,0,1,0 -BRDA:333,0,2,10 -DA:335,19 -BRDA:335,0,0,12 -BRDA:335,0,1,19 -BRDA:335,0,2,7 -BRDA:335,0,3,5 -DA:338,11 -BRDA:338,0,0,11 +DA:323,21 +DA:324,21 +DA:325,21 +DA:328,10 +DA:330,10 +DA:333,31 +BRDA:333,0,0,0 +BRDA:333,0,1,31 +DA:334,28 +BRDA:334,0,0,3 +BRDA:334,0,1,28 +DA:335,21 +BRDA:335,0,0,21 +BRDA:335,0,1,0 +DA:336,10 +DA:337,10 +BRDA:337,0,0,10 +BRDA:337,0,1,3 +BRDA:337,0,2,7 +DA:338,10 +BRDA:338,0,0,10 BRDA:338,0,1,0 -DA:344,11 -BRDA:344,0,0,10 -BRDA:344,0,1,11 -DA:347,11 -DA:348,10 -BRDA:348,0,0,0 -BRDA:348,0,1,1 -BRDA:348,0,2,1 -BRDA:348,0,3,10 -DA:349,10 -DA:351,11 -BRDA:351,0,0,11 -BRDA:351,0,1,1 -BRDA:351,0,2,10 -DA:354,55 -BRDA:354,0,0,11 -BRDA:354,0,1,55 -DA:355,55 -DA:357,44 -BRDA:357,0,0,11 -BRDA:357,0,1,11 -BRDA:357,0,2,33 -BRDA:357,0,3,44 -DA:358,44 -DA:361,11 -BRDA:361,0,0,0 -BRDA:361,0,1,11 -DA:362,11 -BRF:79 -BRH:32 +BRDA:338,0,2,10 +DA:340,19 +BRDA:340,0,0,12 +BRDA:340,0,1,19 +BRDA:340,0,2,7 +BRDA:340,0,3,5 +DA:343,11 +BRDA:343,0,0,11 +BRDA:343,0,1,0 +DA:349,11 +BRDA:349,0,0,10 +BRDA:349,0,1,11 +DA:352,11 +DA:353,10 +BRDA:353,0,0,0 +BRDA:353,0,1,1 +BRDA:353,0,2,1 +BRDA:353,0,3,10 +DA:354,10 +DA:356,11 +BRDA:356,0,0,11 +BRDA:356,0,1,1 +BRDA:356,0,2,10 +DA:359,55 +BRDA:359,0,0,11 +BRDA:359,0,1,55 +DA:360,55 +DA:362,44 +BRDA:362,0,0,11 +BRDA:362,0,1,11 +BRDA:362,0,2,33 +BRDA:362,0,3,44 +DA:363,44 +DA:366,11 +BRDA:366,0,0,0 +BRDA:366,0,1,11 +DA:367,11 +DA:370,10 +BRDA:370,0,0,1 +BRDA:370,0,1,10 +DA:373,10 +DA:374,9 +BRDA:374,0,0,1 +BRDA:374,0,1,9 +BRF:83 +BRH:33 end_of_record diff --git a/test_regress/t/t_cover_line_trace.out b/test_regress/t/t_cover_line_trace.out index 7403fb79e..42cced2fa 100644 --- a/test_regress/t/t_cover_line_trace.out +++ b/test_regress/t/t_cover_line_trace.out @@ -1,43 +1,43 @@ $version Generated by VerilatedVcd $end $timescale 1ps $end $scope module top $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $scope module t $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end $var wire 32 # vlCoverageLineTrace_t_cover_line__15_block [31:0] $end $var wire 32 > cyc [31:0] $end $var wire 32 $ vlCoverageLineTrace_t_cover_line__18_block [31:0] $end $var wire 8 ? cyc_copy [7:0] $end $scope module b1 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end - $var wire 32 ,! vlCoverageLineTrace_t_cover_line__164_block [31:0] $end - $var wire 32 -! vlCoverageLineTrace_t_cover_line__166_else [31:0] $end - $var wire 32 N! vlCoverageLineTrace_t_cover_line__166_if [31:0] $end - $var wire 32 .! vlCoverageLineTrace_t_cover_line__170_else [31:0] $end - $var wire 32 /! vlCoverageLineTrace_t_cover_line__170_if [31:0] $end - $var wire 32 0! vlCoverageLineTrace_t_cover_line__174_else [31:0] $end + $var wire 32 2! vlCoverageLineTrace_t_cover_line__164_block [31:0] $end + $var wire 32 3! vlCoverageLineTrace_t_cover_line__166_else [31:0] $end + $var wire 32 T! vlCoverageLineTrace_t_cover_line__166_if [31:0] $end + $var wire 32 4! vlCoverageLineTrace_t_cover_line__170_else [31:0] $end + $var wire 32 5! vlCoverageLineTrace_t_cover_line__170_if [31:0] $end + $var wire 32 6! vlCoverageLineTrace_t_cover_line__174_else [31:0] $end $upscope $end $scope module b2 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end - $var wire 32 1! vlCoverageLineTrace_t_cover_line__164_block [31:0] $end - $var wire 32 2! vlCoverageLineTrace_t_cover_line__166_else [31:0] $end - $var wire 32 O! vlCoverageLineTrace_t_cover_line__166_if [31:0] $end - $var wire 32 3! vlCoverageLineTrace_t_cover_line__170_else [31:0] $end - $var wire 32 4! vlCoverageLineTrace_t_cover_line__170_if [31:0] $end - $var wire 32 5! vlCoverageLineTrace_t_cover_line__174_else [31:0] $end + $var wire 32 7! vlCoverageLineTrace_t_cover_line__164_block [31:0] $end + $var wire 32 8! vlCoverageLineTrace_t_cover_line__166_else [31:0] $end + $var wire 32 U! vlCoverageLineTrace_t_cover_line__166_if [31:0] $end + $var wire 32 9! vlCoverageLineTrace_t_cover_line__170_else [31:0] $end + $var wire 32 :! vlCoverageLineTrace_t_cover_line__170_if [31:0] $end + $var wire 32 ;! vlCoverageLineTrace_t_cover_line__174_else [31:0] $end $upscope $end $scope module t1 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end - $var wire 32 6! vlCoverageLineTrace_t_cover_line__215_block [31:0] $end - $var wire 32 ;! vlCoverageLineTrace_t_cover_line__219_block [31:0] $end - $var wire 32 ! vlCoverageLineTrace_t_cover_line__225_if [31:0] $end + $var wire 32 ! clk $end $var wire 1 = toggle $end $var wire 32 X vlCoverageLineTrace_t_cover_line__140_block [31:0] $end $var wire 32 Y vlCoverageLineTrace_t_cover_line__141_else [31:0] $end @@ -77,7 +77,7 @@ $timescale 1ps $end $var wire 32 [ vlCoverageLineTrace_t_cover_line__145_else [31:0] $end $upscope $end $scope module a2 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end $var wire 32 \ vlCoverageLineTrace_t_cover_line__140_block [31:0] $end $var wire 32 ] vlCoverageLineTrace_t_cover_line__141_else [31:0] $end @@ -85,7 +85,7 @@ $timescale 1ps $end $var wire 32 _ vlCoverageLineTrace_t_cover_line__145_else [31:0] $end $upscope $end $scope module cond1 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 32 > cyc [31:0] $end $var wire 1 2 a $end $var wire 1 3 b $end @@ -96,86 +96,92 @@ $timescale 1ps $end $var wire 1 + g $end $var wire 1 b h $end $var wire 1 c k $end - $var wire 1 C! l $end - $var wire 1 :! m $end + $var wire 1 I! l $end + $var wire 1 @! m $end $var wire 6 , tab [5:0] $end $var wire 8 d data[0][0] [7:0] $end $var wire 8 e data[0][1] [7:0] $end $var wire 8 f data[1][0] [7:0] $end $var wire 8 g data[1][1] [7:0] $end $var wire 32 % vlCoverageLineTrace_t_cover_line__315_block [31:0] $end - $var wire 32 - vlCoverageLineTrace_t_cover_line__318_block [31:0] $end - $var wire 32 h vlCoverageLineTrace_t_cover_line__323_block [31:0] $end - $var wire 8 D! get_arr__Vstatic__arr[0] [7:0] $end - $var wire 8 E! get_arr__Vstatic__arr[1] [7:0] $end - $var wire 32 5 vlCoverageLineTrace_t_cover_line__328_cond_else [31:0] $end - $var wire 32 6 vlCoverageLineTrace_t_cover_line__328_cond_then [31:0] $end - $var wire 32 7 vlCoverageLineTrace_t_cover_line__329_cond_else [31:0] $end - $var wire 32 8 vlCoverageLineTrace_t_cover_line__329_cond_then [31:0] $end - $var wire 32 . vlCoverageLineTrace_t_cover_line__330_cond_else [31:0] $end - $var wire 32 / vlCoverageLineTrace_t_cover_line__330_cond_then [31:0] $end - $var wire 32 i vlCoverageLineTrace_t_cover_line__331_block [31:0] $end - $var wire 32 j vlCoverageLineTrace_t_cover_line__332_cond_else [31:0] $end - $var wire 32 k vlCoverageLineTrace_t_cover_line__332_cond_then [31:0] $end - $var wire 32 l vlCoverageLineTrace_t_cover_line__333_cond_else [31:0] $end - $var wire 32 m vlCoverageLineTrace_t_cover_line__333_cond_then [31:0] $end - $var wire 32 9 vlCoverageLineTrace_t_cover_line__335_cond_else_1 [31:0] $end - $var wire 32 : vlCoverageLineTrace_t_cover_line__335_cond_then_1 [31:0] $end - $var wire 32 ; vlCoverageLineTrace_t_cover_line__335_cond_else [31:0] $end - $var wire 32 < vlCoverageLineTrace_t_cover_line__335_cond_then [31:0] $end - $var wire 32 n vlCoverageLineTrace_t_cover_line__338_cond_else [31:0] $end - $var wire 32 o vlCoverageLineTrace_t_cover_line__338_cond_then [31:0] $end - $var wire 32 F! vlCoverageLineTrace_t_cover_line__344_cond_else [31:0] $end - $var wire 32 0 vlCoverageLineTrace_t_cover_line__344_cond_else_1 [31:0] $end - $var wire 32 G! vlCoverageLineTrace_t_cover_line__344_cond_then [31:0] $end - $var wire 32 1 vlCoverageLineTrace_t_cover_line__344_cond_then_1 [31:0] $end - $var wire 32 & vlCoverageLineTrace_t_cover_line__347_block [31:0] $end - $var wire 32 p vlCoverageLineTrace_t_cover_line__348_else [31:0] $end - $var wire 32 q vlCoverageLineTrace_t_cover_line__348_if [31:0] $end - $var wire 32 r vlCoverageLineTrace_t_cover_line__348_cond_else [31:0] $end - $var wire 32 s vlCoverageLineTrace_t_cover_line__348_cond_then [31:0] $end - $var wire 32 t vlCoverageLineTrace_t_cover_line__351_cond_else [31:0] $end - $var wire 32 u vlCoverageLineTrace_t_cover_line__351_cond_then [31:0] $end - $var wire 32 ' vlCoverageLineTrace_t_cover_line__354_block [31:0] $end - $var wire 32 v vlCoverageLineTrace_t_cover_line__357_block [31:0] $end - $var wire 32 w vlCoverageLineTrace_t_cover_line__357_cond_else [31:0] $end - $var wire 32 x vlCoverageLineTrace_t_cover_line__357_cond_then [31:0] $end - $var wire 32 y vlCoverageLineTrace_t_cover_line__361_else [31:0] $end - $var wire 32 z vlCoverageLineTrace_t_cover_line__361_if [31:0] $end + $var wire 32 h pstruct [31:0] $end + $var wire 32 - vlCoverageLineTrace_t_cover_line__323_block [31:0] $end + $var wire 32 i vlCoverageLineTrace_t_cover_line__328_block [31:0] $end + $var wire 8 J! get_arr__Vstatic__arr[0] [7:0] $end + $var wire 8 K! get_arr__Vstatic__arr[1] [7:0] $end + $var wire 32 5 vlCoverageLineTrace_t_cover_line__333_cond_else [31:0] $end + $var wire 32 6 vlCoverageLineTrace_t_cover_line__333_cond_then [31:0] $end + $var wire 32 7 vlCoverageLineTrace_t_cover_line__334_cond_else [31:0] $end + $var wire 32 8 vlCoverageLineTrace_t_cover_line__334_cond_then [31:0] $end + $var wire 32 . vlCoverageLineTrace_t_cover_line__335_cond_else [31:0] $end + $var wire 32 / vlCoverageLineTrace_t_cover_line__335_cond_then [31:0] $end + $var wire 32 j vlCoverageLineTrace_t_cover_line__336_block [31:0] $end + $var wire 32 k vlCoverageLineTrace_t_cover_line__337_cond_else [31:0] $end + $var wire 32 l vlCoverageLineTrace_t_cover_line__337_cond_then [31:0] $end + $var wire 32 m vlCoverageLineTrace_t_cover_line__338_cond_else [31:0] $end + $var wire 32 n vlCoverageLineTrace_t_cover_line__338_cond_then [31:0] $end + $var wire 32 9 vlCoverageLineTrace_t_cover_line__340_cond_else_1 [31:0] $end + $var wire 32 : vlCoverageLineTrace_t_cover_line__340_cond_then_1 [31:0] $end + $var wire 32 ; vlCoverageLineTrace_t_cover_line__340_cond_else [31:0] $end + $var wire 32 < vlCoverageLineTrace_t_cover_line__340_cond_then [31:0] $end + $var wire 32 o vlCoverageLineTrace_t_cover_line__343_cond_else [31:0] $end + $var wire 32 p vlCoverageLineTrace_t_cover_line__343_cond_then [31:0] $end + $var wire 32 L! vlCoverageLineTrace_t_cover_line__349_cond_else [31:0] $end + $var wire 32 0 vlCoverageLineTrace_t_cover_line__349_cond_else_1 [31:0] $end + $var wire 32 M! vlCoverageLineTrace_t_cover_line__349_cond_then [31:0] $end + $var wire 32 1 vlCoverageLineTrace_t_cover_line__349_cond_then_1 [31:0] $end + $var wire 32 & vlCoverageLineTrace_t_cover_line__352_block [31:0] $end + $var wire 32 q vlCoverageLineTrace_t_cover_line__353_else [31:0] $end + $var wire 32 r vlCoverageLineTrace_t_cover_line__353_if [31:0] $end + $var wire 32 s vlCoverageLineTrace_t_cover_line__353_cond_else [31:0] $end + $var wire 32 t vlCoverageLineTrace_t_cover_line__353_cond_then [31:0] $end + $var wire 32 u vlCoverageLineTrace_t_cover_line__356_cond_else [31:0] $end + $var wire 32 v vlCoverageLineTrace_t_cover_line__356_cond_then [31:0] $end + $var wire 32 ' vlCoverageLineTrace_t_cover_line__359_block [31:0] $end + $var wire 32 w vlCoverageLineTrace_t_cover_line__362_block [31:0] $end + $var wire 32 x vlCoverageLineTrace_t_cover_line__362_cond_else [31:0] $end + $var wire 32 y vlCoverageLineTrace_t_cover_line__362_cond_then [31:0] $end + $var wire 32 z vlCoverageLineTrace_t_cover_line__366_else [31:0] $end + $var wire 32 { vlCoverageLineTrace_t_cover_line__366_if [31:0] $end + $var wire 32 | vlCoverageLineTrace_t_cover_line__370_cond_else [31:0] $end + $var wire 32 } vlCoverageLineTrace_t_cover_line__370_cond_then [31:0] $end + $var wire 32 ~ vlCoverageLineTrace_t_cover_line__373_block [31:0] $end + $var wire 32 !! vlCoverageLineTrace_t_cover_line__374_else [31:0] $end + $var wire 32 "! vlCoverageLineTrace_t_cover_line__374_if [31:0] $end $scope module unnamedblk1 $end - $var wire 32 H! i [31:0] $end + $var wire 32 N! i [31:0] $end $upscope $end $scope module unnamedblk2 $end - $var wire 32 { i [31:0] $end + $var wire 32 #! i [31:0] $end $upscope $end $upscope $end $scope module o1 $end - $var wire 1 8! clk $end + $var wire 1 >! clk $end $var wire 1 = toggle $end - $var wire 32 | vlCoverageLineTrace_t_cover_line__253_block [31:0] $end - $var wire 32 } vlCoverageLineTrace_t_cover_line__254_else [31:0] $end - $var wire 32 ~ vlCoverageLineTrace_t_cover_line__254_if [31:0] $end - $var wire 32 !! vlCoverageLineTrace_t_cover_line__257_else [31:0] $end - $var wire 32 I! vlCoverageLineTrace_t_cover_line__257_if [31:0] $end + $var wire 32 $! vlCoverageLineTrace_t_cover_line__253_block [31:0] $end + $var wire 32 %! vlCoverageLineTrace_t_cover_line__254_else [31:0] $end + $var wire 32 &! vlCoverageLineTrace_t_cover_line__254_if [31:0] $end + $var wire 32 '! vlCoverageLineTrace_t_cover_line__257_else [31:0] $end + $var wire 32 O! vlCoverageLineTrace_t_cover_line__257_if [31:0] $end $upscope $end $scope module par1 $end - $var wire 32 J! CALLS_FUNC [31:0] $end - $var wire 32 K! vlCoverageLineTrace_t_cover_line__288_block [31:0] $end - $var wire 32 L! vlCoverageLineTrace_t_cover_line__289_else [31:0] $end - $var wire 32 M! vlCoverageLineTrace_t_cover_line__289_if [31:0] $end + $var wire 32 P! CALLS_FUNC [31:0] $end + $var wire 32 Q! vlCoverageLineTrace_t_cover_line__288_block [31:0] $end + $var wire 32 R! vlCoverageLineTrace_t_cover_line__289_else [31:0] $end + $var wire 32 S! vlCoverageLineTrace_t_cover_line__289_if [31:0] $end $upscope $end $scope module tab1 $end - $var wire 1 8! clk $end - $var wire 4 "! cyc4 [3:0] $end - $var wire 32 #! decoded [31:0] $end - $var wire 32 $! vlCoverageLineTrace_t_cover_line__266_block [31:0] $end - $var wire 32 %! vlCoverageLineTrace_t_cover_line__268_case [31:0] $end - $var wire 32 &! vlCoverageLineTrace_t_cover_line__269_case [31:0] $end - $var wire 32 '! vlCoverageLineTrace_t_cover_line__270_case [31:0] $end - $var wire 32 (! vlCoverageLineTrace_t_cover_line__271_case [31:0] $end - $var wire 32 )! vlCoverageLineTrace_t_cover_line__272_case [31:0] $end - $var wire 32 *! vlCoverageLineTrace_t_cover_line__273_case [31:0] $end - $var wire 32 +! vlCoverageLineTrace_t_cover_line__277_block [31:0] $end + $var wire 1 >! clk $end + $var wire 4 (! cyc4 [3:0] $end + $var wire 32 )! decoded [31:0] $end + $var wire 32 *! vlCoverageLineTrace_t_cover_line__266_block [31:0] $end + $var wire 32 +! vlCoverageLineTrace_t_cover_line__268_case [31:0] $end + $var wire 32 ,! vlCoverageLineTrace_t_cover_line__269_case [31:0] $end + $var wire 32 -! vlCoverageLineTrace_t_cover_line__270_case [31:0] $end + $var wire 32 .! vlCoverageLineTrace_t_cover_line__271_case [31:0] $end + $var wire 32 /! vlCoverageLineTrace_t_cover_line__272_case [31:0] $end + $var wire 32 0! vlCoverageLineTrace_t_cover_line__273_case [31:0] $end + $var wire 32 1! vlCoverageLineTrace_t_cover_line__277_block [31:0] $end $upscope $end $upscope $end $scope module my_pkg $end @@ -256,37 +262,37 @@ b00000000 d b00000000 e b00000000 f b00000000 g -b00000000000000000000000000000001 h -b00000000000000000000000000000000 i +b00000000000000100000000000000000 h +b00000000000000000000000000000001 i b00000000000000000000000000000000 j b00000000000000000000000000000000 k b00000000000000000000000000000000 l b00000000000000000000000000000000 m b00000000000000000000000000000000 n -b00000000000000000000000000000001 o +b00000000000000000000000000000000 o b00000000000000000000000000000001 p -b00000000000000000000000000000000 q +b00000000000000000000000000000001 q b00000000000000000000000000000000 r b00000000000000000000000000000000 s -b00000000000000000000000000000001 t -b00000000000000000000000000000000 u -b00000000000000000000000000000100 v -b00000000000000000000000000000011 w -b00000000000000000000000000000001 x +b00000000000000000000000000000000 t +b00000000000000000000000000000001 u +b00000000000000000000000000000000 v +b00000000000000000000000000000100 w +b00000000000000000000000000000011 x b00000000000000000000000000000001 y -b00000000000000000000000000000000 z -b00000000000000000000000000000111 { +b00000000000000000000000000000001 z +b00000000000000000000000000000000 { b00000000000000000000000000000000 | -b00000000000000000000000000000000 } +b00000000000000000000000000000001 } b00000000000000000000000000000000 ~ b00000000000000000000000000000000 !! -b0000 "! -b00000000000000000000000000000000 #! +b00000000000000000000000000000000 "! +b00000000000000000000000000000111 #! b00000000000000000000000000000000 $! b00000000000000000000000000000000 %! b00000000000000000000000000000000 &! b00000000000000000000000000000000 '! -b00000000000000000000000000000000 (! +b0000 (! b00000000000000000000000000000000 )! b00000000000000000000000000000000 *! b00000000000000000000000000000000 +! @@ -302,30 +308,36 @@ b00000000000000000000000000000000 4! b00000000000000000000000000000000 5! b00000000000000000000000000000000 6! b00000000000000000000000000000000 7! -08! +b00000000000000000000000000000000 8! b00000000000000000000000000000000 9! -0:! +b00000000000000000000000000000000 :! b00000000000000000000000000000000 ;! b00000000000000000000000000000000 ! +0>! b00000000000000000000000000000000 ?! -b00000000000000000000000000000000 @! +0@! b00000000000000000000000000000000 A! b00000000000000000000000000000000 B! -0C! -b00000000 D! -b00000000 E! +b00000000000000000000000000000000 C! +b00000000000000000000000000000000 D! +b00000000000000000000000000000000 E! b00000000000000000000000000000000 F! b00000000000000000000000000000000 G! -b00000000000000000000000000000101 H! -b00000000000000000000000000000000 I! -b00000000000000000000000000000010 J! -b00000000000000000000000000000000 K! +b00000000000000000000000000000000 H! +0I! +b00000000 J! +b00000000 K! b00000000000000000000000000000000 L! b00000000000000000000000000000000 M! -b00000000000000000000000000000000 N! +b00000000000000000000000000000101 N! b00000000000000000000000000000000 O! +b00000000000000000000000000000010 P! +b00000000000000000000000000000000 Q! +b00000000000000000000000000000000 R! +b00000000000000000000000000000000 S! +b00000000000000000000000000000000 T! +b00000000000000000000000000000000 U! #10 1* 1+ @@ -361,35 +373,39 @@ b00000000000000000000000000000001 ] b00000000000000000000000000000001 _ b00000010 d b00000001 e -b00000000000000000000000000000001 i +b00000000000000110000000000000000 h b00000000000000000000000000000001 j -b00000000000000000000000000000001 l -b00000000000000000000000000000010 o +b00000000000000000000000000000001 k +b00000000000000000000000000000001 m b00000000000000000000000000000010 p -b00000000000000000000000000000001 u -b00000000000000000000000000001000 v -b00000000000000000000000000000110 w -b00000000000000000000000000000010 x +b00000000000000000000000000000010 q +b00000000000000000000000000000001 v +b00000000000000000000000000001000 w +b00000000000000000000000000000110 x b00000000000000000000000000000010 y +b00000000000000000000000000000010 z b00000000000000000000000000000001 | -b00000000000000000000000000000001 } -b0001 "! +b00000000000000000000000000000001 ~ +b00000000000000000000000000000001 !! b00000000000000000000000000000001 $! +b00000000000000000000000000000001 %! +b0001 (! b00000000000000000000000000000001 *! -b00000000000000000000000000000001 +! -b00000000000000000000000000000001 ,! -b00000000000000000000000000000001 -! -b00000000000000000000000000000001 .! b00000000000000000000000000000001 0! b00000000000000000000000000000001 1! b00000000000000000000000000000001 2! b00000000000000000000000000000001 3! -b00000000000000000000000000000001 5! +b00000000000000000000000000000001 4! b00000000000000000000000000000001 6! b00000000000000000000000000000001 7! -18! +b00000000000000000000000000000001 8! +b00000000000000000000000000000001 9! b00000000000000000000000000000001 ;! b00000000000000000000000000000001 ! +b00000000000000000000000000000001 A! +b00000000000000000000000000000001 B! #15 0* 0+ @@ -399,7 +415,7 @@ b00000000000000000000000000000011 0 b00000000000000000000000000000101 5 b00000000000000000000000000000010 7 b00000000000000000000000000000010 9 -08! +0>! #20 1* 1+ @@ -430,37 +446,40 @@ b00000000000000000000000000000010 ] b00000000000000000000000000000010 _ b00000000 d b00000000 e -b00000000000000000000000000000010 h b00000000000000000000000000000010 i b00000000000000000000000000000010 j -b00000000000000000000000000000010 l -b00000000000000000000000000000011 o +b00000000000000000000000000000010 k +b00000000000000000000000000000010 m b00000000000000000000000000000011 p -b00000000000000000000000000000010 t -b00000000000000000000000000001100 v -b00000000000000000000000000001001 w -b00000000000000000000000000000011 x +b00000000000000000000000000000011 q +b00000000000000000000000000000010 u +b00000000000000000000000000001100 w +b00000000000000000000000000001001 x b00000000000000000000000000000011 y +b00000000000000000000000000000011 z b00000000000000000000000000000010 | -b00000000000000000000000000000010 } -b0010 "! -b00000000000000000000000000001010 #! +b00000000000000000000000000000010 ~ +b00000000000000000000000000000001 "! b00000000000000000000000000000010 $! -b00000000000000000000000000000001 %! -b00000000000000000000000000000010 +! -b00000000000000000000000000000010 ,! -b00000000000000000000000000000010 -! -b00000000000000000000000000000010 .! -b00000000000000000000000000000010 0! +b00000000000000000000000000000010 %! +b0010 (! +b00000000000000000000000000001010 )! +b00000000000000000000000000000010 *! +b00000000000000000000000000000001 +! b00000000000000000000000000000010 1! b00000000000000000000000000000010 2! b00000000000000000000000000000010 3! -b00000000000000000000000000000010 5! +b00000000000000000000000000000010 4! b00000000000000000000000000000010 6! b00000000000000000000000000000010 7! -18! +b00000000000000000000000000000010 8! +b00000000000000000000000000000010 9! b00000000000000000000000000000010 ;! b00000000000000000000000000000010 ! +b00000000000000000000000000000010 A! +b00000000000000000000000000000010 B! #25 0* 0+ @@ -470,7 +489,7 @@ b00000000000000000000000000000100 0 b00000000000000000000000000001000 5 b00000000000000000000000000000101 7 b00000000000000000000000000000101 9 -08! +0>! #30 1* 1+ @@ -503,37 +522,40 @@ b00000000000000000000000000000011 \ b00000000000000000000000000000011 ] b00000000000000000000000000000011 _ 1` -b00000000000000000000000000000011 h b00000000000000000000000000000011 i -b00000000000000000000000000000001 k -b00000000000000000000000000000011 l -b00000000000000000000000000000100 o +b00000000000000000000000000000011 j +b00000000000000000000000000000001 l +b00000000000000000000000000000011 m b00000000000000000000000000000100 p -b00000000000000000000000000000011 t -b00000000000000000000000000010000 v -b00000000000000000000000000001100 w -b00000000000000000000000000000100 x +b00000000000000000000000000000100 q +b00000000000000000000000000000011 u +b00000000000000000000000000010000 w +b00000000000000000000000000001100 x b00000000000000000000000000000100 y +b00000000000000000000000000000100 z b00000000000000000000000000000011 | -b00000000000000000000000000000011 } -b0011 "! -b00000000000000000000000000010100 #! +b00000000000000000000000000000011 ~ +b00000000000000000000000000000010 !! b00000000000000000000000000000011 $! -b00000000000000000000000000000001 &! -b00000000000000000000000000000011 +! -b00000000000000000000000000000011 ,! -b00000000000000000000000000000011 -! -b00000000000000000000000000000011 .! -b00000000000000000000000000000011 0! +b00000000000000000000000000000011 %! +b0011 (! +b00000000000000000000000000010100 )! +b00000000000000000000000000000011 *! +b00000000000000000000000000000001 ,! b00000000000000000000000000000011 1! b00000000000000000000000000000011 2! b00000000000000000000000000000011 3! -b00000000000000000000000000000011 5! +b00000000000000000000000000000011 4! b00000000000000000000000000000011 6! b00000000000000000000000000000011 7! -18! +b00000000000000000000000000000011 8! +b00000000000000000000000000000011 9! b00000000000000000000000000000011 ;! b00000000000000000000000000000011 ! +b00000000000000000000000000000011 A! +b00000000000000000000000000000011 B! #35 0* 0+ @@ -545,7 +567,7 @@ b00000000000000000000000000001011 5 b00000000000000000000000000001000 7 b00000000000000000000000000000101 : b00000000000000000000000000000011 ; -08! +0>! #40 1* 1+ @@ -578,37 +600,40 @@ b00000000000000000000000000000100 \ b00000000000000000000000000000001 ^ 0` 0b -b00000000000000000000000000000100 h b00000000000000000000000000000100 i -b00000000000000000000000000000011 j -b00000000000000000000000000000100 l -b00000000000000000000000000000101 o -b00000000000000000000000000000001 q +b00000000000000000000000000000100 j +b00000000000000000000000000000011 k +b00000000000000000000000000000100 m +b00000000000000000000000000000101 p b00000000000000000000000000000001 r -b00000000000000000000000000000100 t -b00000000000000000000000000010100 v -b00000000000000000000000000001111 w -b00000000000000000000000000000101 x +b00000000000000000000000000000001 s +b00000000000000000000000000000100 u +b00000000000000000000000000010100 w +b00000000000000000000000000001111 x b00000000000000000000000000000101 y +b00000000000000000000000000000101 z b00000000000000000000000000000100 | -b00000000000000000000000000000001 ~ -b00000000000000000000000000000001 !! -b0100 "! -b00000000000000000000000000011110 #! +b00000000000000000000000000000100 ~ +b00000000000000000000000000000011 !! b00000000000000000000000000000100 $! +b00000000000000000000000000000001 &! b00000000000000000000000000000001 '! -b00000000000000000000000000000100 +! -b00000000000000000000000000000100 ,! -b00000000000000000000000000000100 -! -b00000000000000000000000000000001 /! +b0100 (! +b00000000000000000000000000011110 )! +b00000000000000000000000000000100 *! +b00000000000000000000000000000001 -! b00000000000000000000000000000100 1! b00000000000000000000000000000100 2! -b00000000000000000000000000000001 4! -b00000000000000000000000000000100 6! +b00000000000000000000000000000100 3! +b00000000000000000000000000000001 5! b00000000000000000000000000000100 7! -18! -b00000000000000000000000000000100 ;! -b00000000000000000000000000000001 =! +b00000000000000000000000000000100 8! +b00000000000000000000000000000001 :! +b00000000000000000000000000000100 ! +b00000000000000000000000000000100 A! +b00000000000000000000000000000001 C! #45 0* 0+ @@ -618,7 +643,7 @@ b00000000000000000000000000000110 0 b00000000000000000000000000001110 5 b00000000000000000000000000001011 7 b00000000000000000000000000001000 9 -08! +0>! #50 1* 1+ @@ -648,39 +673,42 @@ b00000000000000000000000000000101 \ b00000000000000000000000000000100 ] b00000000000000000000000000000100 _ 1b -b00000000000000000000000000000101 h b00000000000000000000000000000101 i -b00000000000000000000000000000100 j -b00000000000000000000000000000101 l -b00000000000000000000000000000110 o -b00000000000000000000000000000101 p -b00000000000000000000000000000101 t -b00000000000000000000000000011000 v -b00000000000000000000000000010010 w -b00000000000000000000000000000110 x +b00000000000000000000000000000101 j +b00000000000000000000000000000100 k +b00000000000000000000000000000101 m +b00000000000000000000000000000110 p +b00000000000000000000000000000101 q +b00000000000000000000000000000101 u +b00000000000000000000000000011000 w +b00000000000000000000000000010010 x b00000000000000000000000000000110 y +b00000000000000000000000000000110 z b00000000000000000000000000000101 | -b00000000000000000000000000000100 } -b0101 "! -b00000000000000000000000000101000 #! +b00000000000000000000000000000101 ~ +b00000000000000000000000000000100 !! b00000000000000000000000000000101 $! -b00000000000000000000000000000001 (! -b00000000000000000000000000000101 +! -b00000000000000000000000000000101 ,! -b00000000000000000000000000000101 -! -b00000000000000000000000000000100 .! -b00000000000000000000000000000100 0! +b00000000000000000000000000000100 %! +b0101 (! +b00000000000000000000000000101000 )! +b00000000000000000000000000000101 *! +b00000000000000000000000000000001 .! b00000000000000000000000000000101 1! b00000000000000000000000000000101 2! -b00000000000000000000000000000100 3! -b00000000000000000000000000000100 5! -b00000000000000000000000000000101 6! +b00000000000000000000000000000101 3! +b00000000000000000000000000000100 4! +b00000000000000000000000000000100 6! b00000000000000000000000000000101 7! -18! -b00000000000000000000000000000001 9! -b00000000000000000000000000000110 ;! +b00000000000000000000000000000101 8! +b00000000000000000000000000000100 9! +b00000000000000000000000000000100 ;! b00000000000000000000000000000101 ! +b00000000000000000000000000000101 =! +1>! +b00000000000000000000000000000001 ?! +b00000000000000000000000000000110 A! +b00000000000000000000000000000101 B! +b00000000000000000000000000000001 D! #55 0* 0+ @@ -690,7 +718,7 @@ b00000000000000000000000000000111 0 b00000000000000000000000000010001 5 b00000000000000000000000000001110 7 b00000000000000000000000000001011 9 -08! +0>! #60 1* 1+ @@ -722,37 +750,40 @@ b00000000000000000000000000000110 \ b00000000000000000000000000000101 ] b00000000000000000000000000000101 _ 1` -b00000000000000000000000000000110 h b00000000000000000000000000000110 i -b00000000000000000000000000000010 k -b00000000000000000000000000000110 l -b00000000000000000000000000000111 o -b00000000000000000000000000000110 p -b00000000000000000000000000000110 t -b00000000000000000000000000011100 v -b00000000000000000000000000010101 w -b00000000000000000000000000000111 x +b00000000000000000000000000000110 j +b00000000000000000000000000000010 l +b00000000000000000000000000000110 m +b00000000000000000000000000000111 p +b00000000000000000000000000000110 q +b00000000000000000000000000000110 u +b00000000000000000000000000011100 w +b00000000000000000000000000010101 x b00000000000000000000000000000111 y +b00000000000000000000000000000111 z b00000000000000000000000000000110 | -b00000000000000000000000000000101 } -b0110 "! -b00000000000000000000000000110010 #! +b00000000000000000000000000000110 ~ +b00000000000000000000000000000101 !! b00000000000000000000000000000110 $! -b00000000000000000000000000000001 )! -b00000000000000000000000000000110 +! -b00000000000000000000000000000110 ,! -b00000000000000000000000000000110 -! -b00000000000000000000000000000101 .! -b00000000000000000000000000000101 0! +b00000000000000000000000000000101 %! +b0110 (! +b00000000000000000000000000110010 )! +b00000000000000000000000000000110 *! +b00000000000000000000000000000001 /! b00000000000000000000000000000110 1! b00000000000000000000000000000110 2! -b00000000000000000000000000000101 3! -b00000000000000000000000000000101 5! -b00000000000000000000000000000110 6! +b00000000000000000000000000000110 3! +b00000000000000000000000000000101 4! +b00000000000000000000000000000101 6! b00000000000000000000000000000110 7! -18! -b00000000000000000000000000000111 ;! +b00000000000000000000000000000110 8! +b00000000000000000000000000000101 9! +b00000000000000000000000000000101 ;! b00000000000000000000000000000110 ! +b00000000000000000000000000000111 A! +b00000000000000000000000000000110 B! #65 0* 0+ @@ -764,7 +795,7 @@ b00000000000000000000000000010100 5 b00000000000000000000000000010001 7 b00000000000000000000000000001000 : b00000000000000000000000000000100 ; -08! +0>! #70 1* 1+ @@ -797,37 +828,40 @@ b00000000000000000000000000000111 \ b00000000000000000000000000000110 ] b00000000000000000000000000000110 _ 0` -b00000000000000000000000000000111 h b00000000000000000000000000000111 i -b00000000000000000000000000000101 j -b00000000000000000000000000000111 l -b00000000000000000000000000001000 o -b00000000000000000000000000000111 p -b00000000000000000000000000000111 t -b00000000000000000000000000100000 v -b00000000000000000000000000011000 w -b00000000000000000000000000001000 x +b00000000000000000000000000000111 j +b00000000000000000000000000000101 k +b00000000000000000000000000000111 m +b00000000000000000000000000001000 p +b00000000000000000000000000000111 q +b00000000000000000000000000000111 u +b00000000000000000000000000100000 w +b00000000000000000000000000011000 x b00000000000000000000000000001000 y +b00000000000000000000000000001000 z b00000000000000000000000000000111 | -b00000000000000000000000000000110 } -b0111 "! -b00000000000000000000000000000000 #! +b00000000000000000000000000000111 ~ +b00000000000000000000000000000110 !! b00000000000000000000000000000111 $! -b00000000000000000000000000000010 *! -b00000000000000000000000000000111 +! -b00000000000000000000000000000111 ,! -b00000000000000000000000000000111 -! -b00000000000000000000000000000110 .! -b00000000000000000000000000000110 0! +b00000000000000000000000000000110 %! +b0111 (! +b00000000000000000000000000000000 )! +b00000000000000000000000000000111 *! +b00000000000000000000000000000010 0! b00000000000000000000000000000111 1! b00000000000000000000000000000111 2! -b00000000000000000000000000000110 3! -b00000000000000000000000000000110 5! -b00000000000000000000000000000111 6! +b00000000000000000000000000000111 3! +b00000000000000000000000000000110 4! +b00000000000000000000000000000110 6! b00000000000000000000000000000111 7! -18! -b00000000000000000000000000001000 ;! +b00000000000000000000000000000111 8! +b00000000000000000000000000000110 9! +b00000000000000000000000000000110 ;! b00000000000000000000000000000111 ! +b00000000000000000000000000001000 A! +b00000000000000000000000000000111 B! #75 0* 0+ @@ -837,7 +871,7 @@ b00000000000000000000000000001001 0 b00000000000000000000000000010111 5 b00000000000000000000000000010100 7 b00000000000000000000000000001110 9 -08! +0>! #80 1* 1+ @@ -866,36 +900,39 @@ b00000000000000000000000000000111 [ b00000000000000000000000000001000 \ b00000000000000000000000000000111 ] b00000000000000000000000000000111 _ -b00000000000000000000000000001000 h b00000000000000000000000000001000 i -b00000000000000000000000000000110 j -b00000000000000000000000000001000 l -b00000000000000000000000000001001 o -b00000000000000000000000000001000 p -b00000000000000000000000000001000 t -b00000000000000000000000000100100 v -b00000000000000000000000000011011 w -b00000000000000000000000000001001 x +b00000000000000000000000000001000 j +b00000000000000000000000000000110 k +b00000000000000000000000000001000 m +b00000000000000000000000000001001 p +b00000000000000000000000000001000 q +b00000000000000000000000000001000 u +b00000000000000000000000000100100 w +b00000000000000000000000000011011 x b00000000000000000000000000001001 y +b00000000000000000000000000001001 z b00000000000000000000000000001000 | -b00000000000000000000000000000111 } -b1000 "! +b00000000000000000000000000001000 ~ +b00000000000000000000000000000111 !! b00000000000000000000000000001000 $! -b00000000000000000000000000000011 *! -b00000000000000000000000000001000 +! -b00000000000000000000000000001000 ,! -b00000000000000000000000000001000 -! -b00000000000000000000000000000111 .! -b00000000000000000000000000000111 0! +b00000000000000000000000000000111 %! +b1000 (! +b00000000000000000000000000001000 *! +b00000000000000000000000000000011 0! b00000000000000000000000000001000 1! b00000000000000000000000000001000 2! -b00000000000000000000000000000111 3! -b00000000000000000000000000000111 5! -b00000000000000000000000000001000 6! +b00000000000000000000000000001000 3! +b00000000000000000000000000000111 4! +b00000000000000000000000000000111 6! b00000000000000000000000000001000 7! -18! -b00000000000000000000000000001001 ;! +b00000000000000000000000000001000 8! +b00000000000000000000000000000111 9! +b00000000000000000000000000000111 ;! b00000000000000000000000000001000 ! +b00000000000000000000000000001001 A! +b00000000000000000000000000001000 B! #85 0* 0+ @@ -905,7 +942,7 @@ b00000000000000000000000000001010 0 b00000000000000000000000000011010 5 b00000000000000000000000000010111 7 b00000000000000000000000000010001 9 -08! +0>! #90 1* 1+ @@ -937,36 +974,39 @@ b00000000000000000000000000001001 \ b00000000000000000000000000001000 ] b00000000000000000000000000001000 _ 1` -b00000000000000000000000000001001 h b00000000000000000000000000001001 i -b00000000000000000000000000000011 k -b00000000000000000000000000001001 l -b00000000000000000000000000001010 o -b00000000000000000000000000001001 p -b00000000000000000000000000001001 t -b00000000000000000000000000101000 v -b00000000000000000000000000011110 w -b00000000000000000000000000001010 x +b00000000000000000000000000001001 j +b00000000000000000000000000000011 l +b00000000000000000000000000001001 m +b00000000000000000000000000001010 p +b00000000000000000000000000001001 q +b00000000000000000000000000001001 u +b00000000000000000000000000101000 w +b00000000000000000000000000011110 x b00000000000000000000000000001010 y +b00000000000000000000000000001010 z b00000000000000000000000000001001 | -b00000000000000000000000000001000 } -b1001 "! +b00000000000000000000000000001001 ~ +b00000000000000000000000000001000 !! b00000000000000000000000000001001 $! -b00000000000000000000000000000100 *! -b00000000000000000000000000001001 +! -b00000000000000000000000000001001 ,! -b00000000000000000000000000001001 -! -b00000000000000000000000000001000 .! -b00000000000000000000000000001000 0! +b00000000000000000000000000001000 %! +b1001 (! +b00000000000000000000000000001001 *! +b00000000000000000000000000000100 0! b00000000000000000000000000001001 1! b00000000000000000000000000001001 2! -b00000000000000000000000000001000 3! -b00000000000000000000000000001000 5! -b00000000000000000000000000001001 6! +b00000000000000000000000000001001 3! +b00000000000000000000000000001000 4! +b00000000000000000000000000001000 6! b00000000000000000000000000001001 7! -18! -b00000000000000000000000000001010 ;! +b00000000000000000000000000001001 8! +b00000000000000000000000000001000 9! +b00000000000000000000000000001000 ;! b00000000000000000000000000001001 ! +b00000000000000000000000000001010 A! +b00000000000000000000000000001001 B! #95 0* 0+ @@ -978,7 +1018,7 @@ b00000000000000000000000000011101 5 b00000000000000000000000000011010 7 b00000000000000000000000000001011 : b00000000000000000000000000000101 ; -08! +0>! #100 1* 1+ @@ -1011,33 +1051,36 @@ b00000000000000000000000000001010 \ b00000000000000000000000000001001 ] b00000000000000000000000000001001 _ 0` -b00000000000000000000000000001010 h b00000000000000000000000000001010 i -b00000000000000000000000000000111 j -b00000000000000000000000000001010 l -b00000000000000000000000000001011 o -b00000000000000000000000000001010 p -b00000000000000000000000000001010 t -b00000000000000000000000000101100 v -b00000000000000000000000000100001 w -b00000000000000000000000000001011 x +b00000000000000000000000000001010 j +b00000000000000000000000000000111 k +b00000000000000000000000000001010 m +b00000000000000000000000000001011 p +b00000000000000000000000000001010 q +b00000000000000000000000000001010 u +b00000000000000000000000000101100 w +b00000000000000000000000000100001 x b00000000000000000000000000001011 y +b00000000000000000000000000001011 z b00000000000000000000000000001010 | -b00000000000000000000000000001001 } -b1010 "! +b00000000000000000000000000001010 ~ +b00000000000000000000000000001001 !! b00000000000000000000000000001010 $! -b00000000000000000000000000000101 *! -b00000000000000000000000000001010 +! -b00000000000000000000000000001010 ,! -b00000000000000000000000000001010 -! -b00000000000000000000000000001001 .! -b00000000000000000000000000001001 0! +b00000000000000000000000000001001 %! +b1010 (! +b00000000000000000000000000001010 *! +b00000000000000000000000000000101 0! b00000000000000000000000000001010 1! b00000000000000000000000000001010 2! -b00000000000000000000000000001001 3! -b00000000000000000000000000001001 5! -b00000000000000000000000000001010 6! +b00000000000000000000000000001010 3! +b00000000000000000000000000001001 4! +b00000000000000000000000000001001 6! b00000000000000000000000000001010 7! -18! -b00000000000000000000000000001011 ;! +b00000000000000000000000000001010 8! +b00000000000000000000000000001001 9! +b00000000000000000000000000001001 ;! b00000000000000000000000000001010 ! +b00000000000000000000000000001011 A! +b00000000000000000000000000001010 B! From 0eaa9ed14428ea3cd7f661a81d595bdd9bb51117 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Mon, 3 Nov 2025 15:39:23 +0100 Subject: [PATCH 005/246] Fix `--timing` with `--x-initial-edge` (#6603) (#6631) Signed-off-by: Krzysztof Bieganski --- include/verilated_timing.cpp | 5 +++++ test_regress/t/t_timing_initial_edge.py | 18 ++++++++++++++++++ test_regress/t/t_timing_initial_edge.v | 13 +++++++++++++ 3 files changed, 36 insertions(+) create mode 100755 test_regress/t/t_timing_initial_edge.py create mode 100644 test_regress/t/t_timing_initial_edge.v diff --git a/include/verilated_timing.cpp b/include/verilated_timing.cpp index 58c21bdb4..ea819c16d 100644 --- a/include/verilated_timing.cpp +++ b/include/verilated_timing.cpp @@ -80,6 +80,11 @@ void VlDelayScheduler::resume() { } if (!resumed) { + if (m_context.time() == 0) { + // Nothing was scheduled at time 0, but resume() got called due to --x-initial-edge + return; + } + VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Encountered process that should've been resumed at an " "earlier simulation time. Missed a time slot?\n"); diff --git a/test_regress/t/t_timing_initial_edge.py b/test_regress/t/t_timing_initial_edge.py new file mode 100755 index 000000000..2e1f0f617 --- /dev/null +++ b/test_regress/t/t_timing_initial_edge.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary", "--x-initial-edge"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_timing_initial_edge.v b/test_regress/t/t_timing_initial_edge.v new file mode 100644 index 000000000..b1a095085 --- /dev/null +++ b/test_regress/t/t_timing_initial_edge.v @@ -0,0 +1,13 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + initial begin + #10; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From c299b71677647459354b5e786c2aafd4270a6b24 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 3 Nov 2025 18:27:18 -0500 Subject: [PATCH 006/246] Commentary: Changes update --- Changes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index f9b6edbe9..c2394d2fe 100644 --- a/Changes +++ b/Changes @@ -13,7 +13,9 @@ Verilator 5.043 devel **Other:** -* TBD +* Optimize away calls to empty functions (#6626). [Geza Lore] +* Fix `--timing` with `--x-initial-edge` (#6603) (#6631). [Krzysztof Bieganski, Antmicro Ltd.] +* Fix merging of impure assignments in gate optimization (#6629) (#6630). [Geza Lore] Verilator 5.042 2025-11-02 From 1d9c5c2c6b5e177732a5617dec49ea8ade057c4b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 3 Nov 2025 18:36:20 -0500 Subject: [PATCH 007/246] Fix determining Verilator revision when within git submodules without tags. --- Changes | 1 + Makefile.in | 2 +- src/Makefile.in | 1 + src/config_rev | 58 +++++++++++++++++++++++++++++-------------------- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/Changes b/Changes index c2394d2fe..e1dcfea15 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,7 @@ Verilator 5.043 devel * Optimize away calls to empty functions (#6626). [Geza Lore] * Fix `--timing` with `--x-initial-edge` (#6603) (#6631). [Krzysztof Bieganski, Antmicro Ltd.] * Fix merging of impure assignments in gate optimization (#6629) (#6630). [Geza Lore] +* Fix determining Verilator revision when within git submodules without tags. Verilator 5.042 2025-11-02 diff --git a/Makefile.in b/Makefile.in index a1ec1d163..7a8aaf79a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -781,7 +781,7 @@ clean mostlyclean distclean maintainer-clean:: distclean maintainer-clean:: rm -f *.info* *.1 $(INFOS) $(INFOS_OLD) $(VL_INST_MAN_FILES) - rm -f Makefile config.status config.cache config.log TAGS + rm -f Makefile config.status config.cache config.log configure~ TAGS rm -f verilator_bin* verilator_coverage_bin* rm -f bin/verilator_bin* bin/verilator_coverage_bin* rm -f include/verilated.mk include/verilated_config.h diff --git a/src/Makefile.in b/src/Makefile.in index 1fb95ab68..41742f8b0 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -108,6 +108,7 @@ clang-format: maintainer-copy:: clean mostlyclean distclean maintainer-clean:: + -rm -f config_rev.h -rm -rf obj_* *.log *.dmp *.vpd core -rm -f *.o *.d *_gen_* -rm -f *__gen* obj_* diff --git a/src/config_rev b/src/config_rev index 2b32b57d6..8c36d2197 100755 --- a/src/config_rev +++ b/src/config_rev @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # mypy: disallow-untyped-defs -# pylint: disable=C0103,C0114 +# pylint: disable=C0103,C0114,C0116 ###################################################################### # # Copyright 2005-2025 by Wilson Snyder. This program is free software; you @@ -17,32 +17,44 @@ import os import re import sys +from datetime import datetime, timezone + + +def get_rev() -> str: + if 'VERILATOR_SRC_VERSION' in os.environ: + # e.g. Ubuntu package build + return os.environ['VERILATOR_SRC_VERSION'] + + data = os.popen('git describe --always').read() + + result = "vUNKNOWN" + + m = re.search(r'^(v[0-9].*)', data) + if m: + # e.g. in a complate verilator checkout with tags + result = m.group(1) + result = re.sub('_', '.', result) + else: + # e.g. in a sparse submodule checkout without tags + m = re.search(r'^([a-f0-9]+)$', data) + if m: + result = "vUNKNOWN-" + datetime.now(timezone.utc).strftime("%Y%m%d") + "-" + m.group(1) + + data = os.popen('git status').read() + if (re.search('Changed but not updated', data, flags=re.IGNORECASE) + or re.search('Changes to be committed', data, flags=re.IGNORECASE) + or re.search('Changes not staged', data, flags=re.IGNORECASE)): + result += " (mod)" + + return result + + parser = argparse.ArgumentParser() parser.add_argument('directory') Args = parser.parse_args() os.chdir(Args.directory) -if 'VERILATOR_SRC_VERSION' in os.environ: - rev = os.environ['VERILATOR_SRC_VERSION'] -else: - rev = 'UNKNOWN_REV' - -data = os.popen('git describe').read() - -match = re.search(r'^(v[0-9].*)', data) -if match: - rev = match.group(1) -rev = re.sub('_', '.', rev) - -data = os.popen('git status').read() -if (re.search('Changed but not updated', data, flags=re.IGNORECASE) - or re.search('Changes to be committed', data, flags=re.IGNORECASE) - or re.search('Changes not staged', data, flags=re.IGNORECASE)): - rev += " (mod)" - +rev = get_rev() +print("config_rev using " + rev, file=sys.stderr) print("static const char* const DTVERSION_rev = \"" + rev + "\";") - -# Warn after the print, so at least the header has good contents -if re.search('UNKNOWN', rev): - print("%Warning: No git revision found in config_rev.py", file=sys.stderr) From ea75163567ad53eac1624dcc9183a161ac2c764b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 3 Nov 2025 19:59:59 -0500 Subject: [PATCH 008/246] Fix determining Verilator revision when within git submodules without tags. --- src/config_rev | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/config_rev b/src/config_rev index 8c36d2197..863813313 100755 --- a/src/config_rev +++ b/src/config_rev @@ -27,18 +27,23 @@ def get_rev() -> str: data = os.popen('git describe --always').read() - result = "vUNKNOWN" + if 'SOURCE_DATE_EPOCH' in os.environ: + # e.g. Package builds that don't set VERILATOR_SRC_VERSION + stamp = datetime.fromtimestamp(int(os.getenv("SOURCE_DATE_EPOCH", "0")), tz=timezone.utc) + else: + stamp = datetime.now(timezone.utc) + result = "vUNKNOWN-built" + stamp.strftime("%Y%m%d") m = re.search(r'^(v[0-9].*)', data) if m: - # e.g. in a complate verilator checkout with tags + # e.g. in a complate verilator checkout with tags; ignore build time result = m.group(1) result = re.sub('_', '.', result) else: - # e.g. in a sparse submodule checkout without tags + # e.g. in a sparse submodule checkout without tags; append hash m = re.search(r'^([a-f0-9]+)$', data) if m: - result = "vUNKNOWN-" + datetime.now(timezone.utc).strftime("%Y%m%d") + "-" + m.group(1) + result += "-" + m.group(1) data = os.popen('git status').read() if (re.search('Changed but not updated', data, flags=re.IGNORECASE) From 8eed4e32bae41f8919b8cfb632ddbd8129efacb9 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Tue, 4 Nov 2025 14:28:42 +0100 Subject: [PATCH 009/246] Support this.randomize() with constraints (#6634) --- src/V3Randomize.cpp | 17 ++++++++++++++- test_regress/t/t_randomize_this_with.py | 21 +++++++++++++++++++ test_regress/t/t_randomize_this_with.v | 28 +++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_randomize_this_with.py create mode 100644 test_regress/t/t_randomize_this_with.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 5b5064f5d..04e11b48a 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -914,7 +914,22 @@ class ConstraintExprVisitor final : public VNVisitor { if (nodep->user1()) { nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)"); } - editFormat(nodep); + // Handle MemberSel references created by captureRefByThis() + if (VN_IS(nodep->fromp(), VarRef) + && nodep->fromp()->user1() // Depending on a randomized variable + && nodep->user2p() // Pointer to containing module + && VN_AS(nodep->user2p(), NodeModule) == nodep->varp()->user2p()) { + // Convert to VarRef + AstVarRef* const varRefp + = new AstVarRef{nodep->fileline(), nodep->varp(), VAccess::READ}; + varRefp->user1(nodep->varp()->rand().isRandomizable()); + varRefp->classOrPackagep(VN_AS(nodep->user2p(), NodeModule)); + nodep->replaceWith(varRefp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + visit(varRefp); + } else { + editFormat(nodep); + } } void visit(AstSFormatF* nodep) override {} void visit(AstStmtExpr* nodep) override {} diff --git a/test_regress/t/t_randomize_this_with.py b/test_regress/t/t_randomize_this_with.py new file mode 100755 index 000000000..c88b3d0f3 --- /dev/null +++ b/test_regress/t/t_randomize_this_with.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_this_with.v b/test_regress/t/t_randomize_this_with.v new file mode 100644 index 000000000..cc7d3fce5 --- /dev/null +++ b/test_regress/t/t_randomize_this_with.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +class Cls; + rand int v_rand; + int v_norand; + task body; + int x; + v_norand = 42; + x = this.randomize() with {v_rand==0;}; + `checkd(v_rand, 0); + `checkd(v_norand, 42); + endtask +endclass + +module t; + initial begin + Cls c = new; + c.body(); + $finish; + end +endmodule From 5d5798b4af18e5e9990f5b8f6854a7014eb3e9e5 Mon Sep 17 00:00:00 2001 From: Pawel Kojma <95043203+pawel-kojma@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:12:30 +0100 Subject: [PATCH 010/246] Fix parsing of `with` clause inside covergroups (#6618) --- docs/CONTRIBUTORS | 1 + src/verilog.y | 20 +++++--- test_regress/t/t_covergroup_unsup.out | 67 +++++++++++++++++++++------ test_regress/t/t_covergroup_unsup.v | 7 ++- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 8bb9a82b0..968b95e1b 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -190,6 +190,7 @@ Paul Bowen-Huggett Paul Swirhun Paul Wright Pawel Jewstafjew +Pawel Kojma Pawel Sagan Pengcheng Xu Peter Birch diff --git a/src/verilog.y b/src/verilog.y index 624742b5b..32622b103 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -6862,11 +6862,11 @@ bins_or_options: // ==IEEE: bins_or_options // // Can't use wildcardE as results in conflicts | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin specification"); DEL($3, $6, $8); } - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE + | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; BBCOVERIGN($8, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $6, $10, $12); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'wildcard' specification"); DEL($4, $7, $9); } - | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE + | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE { $$ = nullptr; BBCOVERIGN($9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($4, $7, $11, $13); } // // // cgexpr part of trans_list @@ -6980,6 +6980,16 @@ cross_body_item: // ==IEEE: cross_body_item ; select_expression: // ==IEEE: select_expression + select_expression_r + { $$ = $1; } + | select_expression yP_ANDAND select_expression + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); } + | select_expression yP_OROR select_expression + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); } + ; + +// This non-terminal exists to disambiguate select_expression and make "with" bind tighter +select_expression_r: // // IEEE: select_condition expanded here yBINSOF '(' bins_expression ')' { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($3); } @@ -6993,16 +7003,14 @@ select_expression: // ==IEEE: select_expression { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($3); } | '!' yWITH__PAREN '(' cgexpr ')' { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($4); } + | select_expression_r yWITH__PAREN '(' cgexpr ')' + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression with"); DEL($1, $4); } // // IEEE-2012: Need clarification as to precedence //UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } // // IEEE-2012: Need clarification as to precedence //UNSUP '!' yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } // | '(' select_expression ')' { $$ = $2; } - | select_expression yP_ANDAND select_expression - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); } - | select_expression yP_OROR select_expression - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); } // // IEEE-2012: cross_identifier // // Part of covergroup_expression - generic identifier // // IEEE-2012: Need clarification as to precedence diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 5b5988b9c..0284bfa55 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -223,13 +223,13 @@ 118 | { bins ba[2] = {a}; } | ^ %Warning-COVERIGN: t/t_covergroup_unsup.v:120:23: Ignoring unsupported: cover bin 'with' specification - 120 | { bins ba = {a} with { b }; } + 120 | { bins ba = {a} with ( b ); } | ^~~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:122:27: Ignoring unsupported: cover bin 'wildcard' specification 122 | { wildcard bins bwa = {a}; } | ^ %Warning-COVERIGN: t/t_covergroup_unsup.v:123:34: Ignoring unsupported: cover bin 'wildcard' 'with' specification - 123 | { wildcard bins bwaw = {a} with { b }; } + 123 | { wildcard bins bwaw = {a} with ( b ); } | ^~~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:125:20: Ignoring unsupported: cover bin 'default' 125 | { bins def = default; } @@ -381,28 +381,67 @@ %Warning-COVERIGN: t/t_covergroup_unsup.v:157:10: Ignoring unsupported: coverage cross bin 157 | bins bin_or = binsof(a) || binsof(b); | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:158:26: Ignoring unsupported: coverage select expression 'binsof' + 158 | bins bin_with = binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:158:36: Ignoring unsupported: coverage select expression with + 158 | bins bin_with = binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:158:10: Ignoring unsupported: coverage cross bin + 158 | bins bin_with = binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:29: Ignoring unsupported: coverage select expression 'binsof' + 159 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:42: Ignoring unsupported: coverage select expression 'binsof' + 159 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:52: Ignoring unsupported: coverage select expression with + 159 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:39: Ignoring unsupported: coverage select expression '||' + 159 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:10: Ignoring unsupported: coverage cross bin + 159 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:30: Ignoring unsupported: coverage select expression 'binsof' + 160 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:43: Ignoring unsupported: coverage select expression 'binsof' + 160 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:53: Ignoring unsupported: coverage select expression with + 160 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:40: Ignoring unsupported: coverage select expression '&&' + 160 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:10: Ignoring unsupported: coverage cross bin + 160 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:143:7: Ignoring unsupported: cover cross 143 | cross a, b { | ^~~~~ %Warning-COVERIGN: t/t_covergroup_unsup.v:142:4: Ignoring unsupported: covergroup 142 | covergroup cg_cross_bins; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:161:4: Ignoring unsupported: covergroup - 161 | covergroup cgArgs(int cg_lim); +%Warning-COVERIGN: t/t_covergroup_unsup.v:164:4: Ignoring unsupported: covergroup + 164 | covergroup cgArgs(int cg_lim); | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:23: Ignoring unsupported: coverage clocking event - 168 | covergroup cov1 @m_z; +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:23: Ignoring unsupported: coverage clocking event + 171 | covergroup cov1 @m_z; | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:10: Ignoring unsupported: coverpoint - 169 | coverpoint m_x; +%Warning-COVERIGN: t/t_covergroup_unsup.v:172:10: Ignoring unsupported: coverpoint + 172 | coverpoint m_x; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:10: Ignoring unsupported: coverpoint - 170 | coverpoint m_y; +%Warning-COVERIGN: t/t_covergroup_unsup.v:173:10: Ignoring unsupported: coverpoint + 173 | coverpoint m_y; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:7: Ignoring unsupported: covergroup - 168 | covergroup cov1 @m_z; +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Ignoring unsupported: covergroup + 171 | covergroup cov1 @m_z; | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Ignoring unsupported: covergroup - 178 | covergroup extends cg_empty; +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:7: Ignoring unsupported: covergroup + 181 | covergroup extends cg_empty; | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index a8215c20b..a4d4517fa 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -117,10 +117,10 @@ module t (/*AUTOARG*/ { bins ba[] = {a}; } { bins ba[2] = {a}; } - { bins ba = {a} with { b }; } + { bins ba = {a} with ( b ); } { wildcard bins bwa = {a}; } - { wildcard bins bwaw = {a} with { b }; } + { wildcard bins bwaw = {a} with ( b ); } { bins def = default; } { bins defs = default sequence; } @@ -155,6 +155,9 @@ module t (/*AUTOARG*/ bins bin_par = (binsof(a)); bins bin_and = binsof(a) && binsof(b); bins bin_or = binsof(a) || binsof(b); + bins bin_with = binsof(a) with (a); + bins bin_or_with = binsof(a) || binsof(a) with (a); + bins bin_and_with = binsof(a) && binsof(a) with (a); } endgroup From 2c01aff2b3900e9f9d0c9c91e3a3f612f9f13e6d Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Tue, 4 Nov 2025 10:34:58 -0500 Subject: [PATCH 011/246] Fix expression short circuiting (#6483) --- src/V3AstNodes.cpp | 10 ++++++--- test_regress/t/t_expr_shortcircuit.py | 18 ++++++++++++++++ test_regress/t/t_expr_shortcircuit.v | 30 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_expr_shortcircuit.py create mode 100644 test_regress/t/t_expr_shortcircuit.v diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 778784ba0..d7ed985dc 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2942,9 +2942,13 @@ bool AstNodeFTask::getPurityRecurse() const { if (varp->isInoutOrRef()) return false; } if (!stmtp->isPure()) return false; - if (stmtp->exists([](const AstNodeVarRef* const varrefp) { - return (!varrefp->varp()->isFuncLocal() || varrefp->varp()->lifetime().isStatic()) - && varrefp->access().isWriteOrRW(); + if (stmtp->exists([](AstNode* const nodep) { + if (AstNodeVarRef* const varrefp = VN_CAST(nodep, VarRef)) { + return (!varrefp->varp()->isFuncLocal() + || varrefp->varp()->lifetime().isStatic()) + && varrefp->access().isWriteOrRW(); + } + return !nodep->isPure(); })) return false; } diff --git a/test_regress/t/t_expr_shortcircuit.py b/test_regress/t/t_expr_shortcircuit.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_expr_shortcircuit.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_expr_shortcircuit.v b/test_regress/t/t_expr_shortcircuit.v new file mode 100644 index 000000000..f3960ce4c --- /dev/null +++ b/test_regress/t/t_expr_shortcircuit.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + int cyc = 0; + + function automatic logic is_odd(int value); + logic odd = value % 2 == 1; + if (!odd) $error($sformatf("%0d is not odd", value)); + return odd; + endfunction + + always_ff @(posedge clk) begin + if (cyc[0] == 1'b0 || is_odd(cyc)) + cyc <= cyc + 1; + if (cyc == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule From 94d0513bc7fe32935afc87a28f4836fc55c47494 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Tue, 4 Nov 2025 16:11:53 -0500 Subject: [PATCH 012/246] Don't pick initial random values for verilator-created variables (#6611) --- src/V3EmitCFunc.cpp | 1 + test_regress/t/t_split_var_issue.py | 18 +++++++++++ test_regress/t/t_split_var_issue.v | 50 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100755 test_regress/t/t_split_var_issue.py create mode 100644 test_regress/t/t_split_var_issue.v diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 2b879b0c7..b3f9cb962 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -670,6 +670,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing, || varp->isFuncLocal() // Randomization too slow || (basicp && basicp->isZeroInit()) || (v3Global.opt.underlineZero() && !varp->name().empty() && varp->name()[0] == '_') + || (varp->varType().isTemp() && !varp->isXTemp()) || (varp->isXTemp() ? (v3Global.opt.xAssign() != "unique") : (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0"))); diff --git a/test_regress/t/t_split_var_issue.py b/test_regress/t/t_split_var_issue.py new file mode 100755 index 000000000..519ab4887 --- /dev/null +++ b/test_regress/t/t_split_var_issue.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios("simulator_st") + +test.compile(verilator_flags2=["--assert", "-fno-localize"]) + +test.execute(all_run_flags=["+verilator+rand+reset+2"]) + +test.passes() diff --git a/test_regress/t/t_split_var_issue.v b/test_regress/t/t_split_var_issue.v new file mode 100644 index 000000000..0c7be6ebb --- /dev/null +++ b/test_regress/t/t_split_var_issue.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module other_sub ( + input wire clk, + input wire foo, + output logic [5:0] bar +); + always_comb bar[0] = foo; +`ifndef NO_ASSERT + assert property (@(posedge clk) (foo == bar[0])); +`endif + always_ff @(posedge clk) bar[5:1] <= bar[4:0]; +endmodule + +interface intf + (input wire clk); +endinterface + +module sub ( + input logic clk +); + for (genvar k = 0; k < 4; k++) begin + logic [5:0] bar; + other_sub + the_other_sub ( + .clk, + .foo ('1), + .bar + ); + end +endmodule + +module t (/*AUTOARG*/ + clk + ); + input clk; + int cyc = 0; + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + sub the_sub (.*); +endmodule From fe1a9e9ea70c294712381df0b4747765ffce064d Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 4 Nov 2025 23:00:26 +0000 Subject: [PATCH 013/246] Internals: Cleanup V3EmitCSyms (#6635) --- src/V3EmitCSyms.cpp | 737 +++++++++++++++++----------------- src/cppcheck-suppressions.txt | 1 - 2 files changed, 364 insertions(+), 374 deletions(-) diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 891241651..118ae0d61 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -32,6 +32,10 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Symbol table emitting +// Some handy short-hands to reduce verbosity +static constexpr auto symClassName = &EmitCUtil::symClassName; +static constexpr auto topClassName = &EmitCUtil::topClassName; + class EmitCSyms final : EmitCBaseVisitorConst { // NODE STATE // Cleared on Netlist @@ -41,13 +45,13 @@ class EmitCSyms final : EmitCBaseVisitorConst { // TYPES struct ScopeData final { const AstNode* m_nodep; - const string m_symName; - const string m_prettyName; - const string m_defName; + const std::string m_symName; + const std::string m_prettyName; + const std::string m_defName; const int m_timeunit; - string m_type; - ScopeData(const AstNode* nodep, const string& symName, const string& prettyName, - const string& defName, int timeunit, const string& type) + std::string m_type; // TODO: this should be an enum + ScopeData(const AstNode* nodep, const std::string& symName, const std::string& prettyName, + const std::string& defName, int timeunit, const std::string& type) : m_nodep{nodep} , m_symName{symName} , m_prettyName{prettyName} @@ -56,22 +60,22 @@ class EmitCSyms final : EmitCBaseVisitorConst { , m_type{type} {} }; struct ScopeFuncData final { - AstScopeName* const m_scopep; - AstCFunc* const m_cfuncp; - AstNodeModule* const m_modp; - ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp) + const AstScopeName* const m_scopep; + const AstCFunc* const m_cfuncp; + const AstNodeModule* const m_modp; + ScopeFuncData(const AstScopeName* scopep, const AstCFunc* funcp, const AstNodeModule* modp) : m_scopep{scopep} , m_cfuncp{funcp} , m_modp{modp} {} }; struct ScopeVarData final { - const string m_scopeName; - const string m_varBasePretty; - AstVar* const m_varp; + const std::string m_scopeName; + const std::string m_varBasePretty; + const AstVar* const m_varp; const AstNodeModule* const m_modp; - AstScope* const m_scopep; - ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, - const AstNodeModule* modp, AstScope* scopep) + const AstScope* const m_scopep; + ScopeVarData(const std::string& scopeName, const std::string& varBasePretty, + const AstVar* varp, const AstNodeModule* modp, const AstScope* scopep) : m_scopeName{scopeName} , m_varBasePretty{varBasePretty} , m_varp{varp} @@ -79,23 +83,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { , m_scopep{scopep} {} }; using ScopeNames = std::map; - using ScopeModPair = std::pair; - using ModVarPair = std::pair; - using ScopeNameList = std::vector; - using ScopeNameHierarchy = std::map; - struct CmpName final { - bool operator()(const ScopeModPair& lhsp, const ScopeModPair& rhsp) const { - return lhsp.first->name() < rhsp.first->name(); - } - }; - struct CmpDpi final { - bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const { - if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) { - return rhsp->dpiImportPrototype(); - } - return lhsp->name() < rhsp->name(); - } - }; + using ScopeModPair = std::pair; + using ModVarPair = std::pair; // STATE AstCFunc* m_cfuncp = nullptr; // Current function @@ -108,14 +97,17 @@ class EmitCSyms final : EmitCBaseVisitorConst { ScopeNames m_scopeNames; // Each unique AstScopeName. Dpi scopes added later ScopeNames m_dpiScopeNames; // Each unique AstScopeName for DPI export ScopeNames m_vpiScopeCandidates; // All scopes for VPI - ScopeNameHierarchy m_vpiScopeHierarchy; // The actual hierarchy of scopes + // The actual hierarchy of scopes + std::map> m_vpiScopeHierarchy; int m_coverBins = 0; // Coverage bin number const bool m_dpiHdrOnly; // Only emit the DPI header int m_numStmts = 0; // Number of statements output - int m_funcNum = 0; // CFunc split function number + size_t m_funcNum = 0; // CFunc split function number V3OutCFile* m_ofpBase = nullptr; // Base (not split) C file + AstCFile* m_ofpBaseFile = nullptr; // Base (not split) AstCFile std::unordered_map m_usesVfinal; // Split method uses __Vfinal VDouble0 m_statVarScopeBytes; // Statistic tracking + const std::string m_symsFileBase = v3Global.opt.makeDir() + "/" + symClassName(); // METHODS void emitSymHdr(); @@ -129,11 +121,10 @@ class EmitCSyms final : EmitCBaseVisitorConst { static void nameCheck(AstNode* nodep) { // Prevent GCC compile time error; name check all things that reach C++ code - if (nodep->name() != "" - && !(VN_IS(nodep, CFunc) - && (VN_AS(nodep, CFunc)->isConstructor() - || VN_AS(nodep, CFunc)->isDestructor()))) { - const string rsvd = V3LanguageWords::isKeyword(nodep->name()); + if (nodep->name().empty()) return; + const AstCFunc* const cfuncp = VN_CAST(nodep, CFunc); + if (!cfuncp || (!cfuncp->isConstructor() && !cfuncp->isDestructor())) { + const std::string rsvd = V3LanguageWords::isKeyword(nodep->name()); if (rsvd != "") { // Generally V3Name should find all of these and throw SYMRSVDWORD. // We'll still check here because the compiler errors @@ -146,19 +137,19 @@ class EmitCSyms final : EmitCBaseVisitorConst { } } - static string scopeSymString(const string& scpname) { - string out = scpname; - string::size_type pos; - while ((pos = out.find("__PVT__")) != string::npos) out.replace(pos, 7, ""); - if (out.substr(0, 10) == "TOP__DOT__") out.replace(0, 10, ""); - if (out.substr(0, 4) == "TOP.") out.replace(0, 4, ""); - while ((pos = out.find('.')) != string::npos) out.replace(pos, 1, "__"); - while ((pos = out.find("__DOT__")) != string::npos) out.replace(pos, 7, "__"); + static std::string scopeSymString(const std::string& scpname) { + std::string out = scpname; + std::string::size_type pos; + while ((pos = out.find("__PVT__")) != std::string::npos) out.replace(pos, 7, ""); + if (VString::startsWith(out, "TOP__DOT__")) out.replace(0, 10, ""); + if (VString::startsWith(out, "TOP.")) out.replace(0, 4, ""); + while ((pos = out.find('.')) != std::string::npos) out.replace(pos, 1, "__"); + while ((pos = out.find("__DOT__")) != std::string::npos) out.replace(pos, 7, "__"); return out; } - static string scopeDecodeIdentifier(const string& scpname) { - string::size_type pos = string::npos; + static string scopeDecodeIdentifier(const std::string& scpname) { + std::string::size_type pos = std::string::npos; // Remove hierarchy size_t i = 0; @@ -173,16 +164,16 @@ class EmitCSyms final : EmitCBaseVisitorConst { } } - return pos != string::npos ? scpname.substr(pos + 1) : scpname; + return pos != std::string::npos ? scpname.substr(pos + 1) : scpname; } /// (scp, m_vpiScopeCandidates, m_scopeNames) -> m_scopeNames /// Look for parent scopes of scp in m_vpiScopeCandidates (separated by __DOT__ or ".") /// Then add/update entry in m_scopeNames if not already there - void varHierarchyScopes(string scp) { + void varHierarchyScopes(std::string scp) { - string::size_type prd_pos = scp.rfind('.'); - string::size_type dot_pos = scp.rfind("__DOT__"); + std::string::size_type prd_pos = scp.rfind('.'); + std::string::size_type dot_pos = scp.rfind("__DOT__"); while (!scp.empty()) { const auto scpit = m_vpiScopeCandidates.find(scopeSymString(scp)); @@ -209,61 +200,60 @@ class EmitCSyms final : EmitCBaseVisitorConst { // We didn't have all m_scopes loaded when we encountered variables, so expand them now // It would be less code if each module inserted its own variables. // Someday. - for (std::vector::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); - ++itsc) { - AstScope* const scopep = itsc->first; - const AstNodeModule* const smodp = itsc->second; - for (std::vector::iterator it = m_modVars.begin(); it != m_modVars.end(); - ++it) { - const AstNodeModule* const modp = it->first; - AstVar* const varp = it->second; - if (modp == smodp) { - // Need to split the module + var name into the - // original-ish full scope and variable name under that scope. - // The module instance name is included later, when we - // know the scopes this module is under - string whole = scopep->name() + "__DOT__" + varp->name(); - string scpName; - string varBase; - if (whole.substr(0, 10) == "__DOT__TOP") whole.replace(0, 10, ""); - const string::size_type dpos = whole.rfind("__DOT__"); - if (dpos != string::npos) { - scpName = whole.substr(0, dpos); - varBase = whole.substr(dpos + std::strlen("__DOT__")); - } else { - varBase = whole; - } - // UINFO(9, "For " << scopep->name() << " - " << varp->name() << " Scp " - // << scpName << "Var " << varBase); - const string varBasePretty = AstNode::vpiName(VName::dehash(varBase)); - const string scpPretty = AstNode::prettyName(VName::dehash(scpName)); - const string scpSym = scopeSymString(VName::dehash(scpName)); - // UINFO(9, " scnameins sp " << scpName << " sp " << scpPretty << " ss " - // << scpSym); - if (v3Global.opt.vpi()) varHierarchyScopes(scpName); - if (m_scopeNames.find(scpSym) == m_scopeNames.end()) { - // cppcheck-suppress stlFindInsert - m_scopeNames.emplace(scpSym, ScopeData{varp, scpSym, scpPretty, "", - 0, "SCOPE_OTHER"}); - } - m_scopeVars.emplace(scpSym + " " + varp->name(), - ScopeVarData{scpSym, varBasePretty, varp, modp, scopep}); + for (const ScopeModPair& smPair : m_scopes) { + const AstScope* const scopep = smPair.first; + const AstNodeModule* const smodp = smPair.second; + for (const ModVarPair& mvPair : m_modVars) { + const AstNodeModule* const modp = mvPair.first; + const AstVar* const varp = mvPair.second; + if (modp != smodp) continue; + + // Need to split the module + var name into the + // original-ish full scope and variable name under that scope. + // The module instance name is included later, when we + // know the scopes this module is under + std::string whole = scopep->name() + "__DOT__" + varp->name(); + std::string scpName; + std::string varBase; + if (VString::startsWith(whole, "__DOT__TOP")) whole.replace(0, 10, ""); + const std::string::size_type dpos = whole.rfind("__DOT__"); + if (dpos != std::string::npos) { + scpName = whole.substr(0, dpos); + varBase = whole.substr(dpos + std::strlen("__DOT__")); + } else { + varBase = whole; } + // UINFO(9, "For " << scopep->name() << " - " << varp->name() << " Scp " + // << scpName << "Var " << varBase); + const std::string varBasePretty = AstNode::vpiName(VName::dehash(varBase)); + const std::string scpPretty = AstNode::prettyName(VName::dehash(scpName)); + const std::string scpSym = scopeSymString(VName::dehash(scpName)); + // UINFO(9, " scnameins sp " << scpName << " sp " << scpPretty << " ss " + // << scpSym); + if (v3Global.opt.vpi()) varHierarchyScopes(scpName); + + m_scopeNames.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(scpSym), // + std::forward_as_tuple(varp, scpSym, scpPretty, "", 0, "SCOPE_OTHER")); + + m_scopeVars.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(scpSym + " " + varp->name()), // + std::forward_as_tuple(scpSym, varBasePretty, varp, modp, scopep)); } } } void buildVpiHierarchy() { - for (ScopeNames::const_iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); - ++it) { - - const string symName = it->second.m_symName; - string above = symName; - if (above.substr(0, 4) == "TOP.") above.replace(0, 4, ""); + for (const auto& itpair : m_scopeNames) { + const std::string symName = itpair.second.m_symName; + std::string above = symName; + if (VString::startsWith(above, "TOP.")) above.replace(0, 4, ""); while (!above.empty()) { - const string::size_type pos = above.rfind("__"); - if (pos == string::npos) break; + const std::string::size_type pos = above.rfind("__"); + if (pos == std::string::npos) break; above.resize(pos); if (m_vpiScopeHierarchy.find(above) != m_vpiScopeHierarchy.end()) { m_vpiScopeHierarchy[above].push_back(symName); @@ -288,8 +278,17 @@ class EmitCSyms final : EmitCBaseVisitorConst { } // Sort by names, so line/process order matters less - stable_sort(m_scopes.begin(), m_scopes.end(), CmpName()); - stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi()); + std::stable_sort(m_scopes.begin(), m_scopes.end(), + [](const ScopeModPair& a, const ScopeModPair& b) { + return a.first->name() < b.first->name(); + }); + std::stable_sort(m_dpis.begin(), m_dpis.end(), // + [](const AstCFunc* ap, const AstCFunc* bp) { + if (ap->dpiImportPrototype() != bp->dpiImportPrototype()) { + return bp->dpiImportPrototype(); + } + return ap->name() < bp->name(); + }); // Output if (!m_dpiHdrOnly) { @@ -310,18 +309,18 @@ class EmitCSyms final : EmitCBaseVisitorConst { iterateChildrenConst(nodep); } void visit(AstCellInlineScope* nodep) override { - if (v3Global.opt.vpi()) { - const string type - = (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE"; - const string name = nodep->scopep()->shortName() + "__DOT__" + nodep->name(); - const string name_pretty = AstNode::vpiName(name); - const int timeunit = m_modp->timeunit().powerOfTen(); - m_vpiScopeCandidates.emplace( - scopeSymString(name), - ScopeData{nodep, scopeSymString(name), name_pretty, - type == "SCOPE_MODULE" ? nodep->origModName() : "", timeunit, - type}); - } + if (!v3Global.opt.vpi()) return; + + const std::string type = (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" // + : "SCOPE_MODULE"; + const std::string name = nodep->scopep()->shortName() + "__DOT__" + nodep->name(); + const int timeunit = m_modp->timeunit().powerOfTen(); + m_vpiScopeCandidates.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(scopeSymString(name)), // + std::forward_as_tuple(nodep, scopeSymString(name), AstNode::vpiName(name), + type == "SCOPE_MODULE" ? nodep->origModName() : "", + timeunit, type)); } void visit(AstScope* nodep) override { if (VN_IS(m_modp, Class)) return; // The ClassPackage is what is visible @@ -330,42 +329,51 @@ class EmitCSyms final : EmitCBaseVisitorConst { m_scopes.emplace_back(nodep, m_modp); if (v3Global.opt.vpi() && !nodep->isTop()) { - const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_PACKAGE" : "SCOPE_MODULE"; - const string name_pretty = AstNode::vpiName(nodep->shortName()); + const std::string type = VN_IS(nodep->modp(), Package) ? "SCOPE_PACKAGE" // + : "SCOPE_MODULE"; const int timeunit = m_modp->timeunit().powerOfTen(); - m_vpiScopeCandidates.emplace(scopeSymString(nodep->name()), - ScopeData{nodep, scopeSymString(nodep->name()), - name_pretty, nodep->modp()->origName(), - timeunit, type}); + m_vpiScopeCandidates.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(scopeSymString(nodep->name())), // + std::forward_as_tuple(nodep, scopeSymString(nodep->name()), + AstNode::vpiName(nodep->shortName()), + nodep->modp()->origName(), timeunit, type)); } iterateChildrenConst(nodep); } void visit(AstScopeName* nodep) override { - const string name = nodep->scopeSymName(); + const std::string name = nodep->scopeSymName(); // UINFO(9, "scnameins sp " << nodep->name() << " sp " << nodep->scopePrettySymName() // << " ss" << name); const int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0; - m_dpiScopeNames.emplace(name, ScopeData{nodep, name, nodep->scopePrettySymName(), "", - timeunit, "SCOPE_OTHER"}); + m_dpiScopeNames.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(name), // + std::forward_as_tuple(nodep, name, nodep->scopePrettySymName(), "", timeunit, + "SCOPE_OTHER")); + if (nodep->dpiExport()) { UASSERT_OBJ(m_cfuncp, nodep, "ScopeName not under DPI function"); - m_scopeFuncs.emplace(name + " " + m_cfuncp->name(), - ScopeFuncData(nodep, m_cfuncp, m_modp)); + m_scopeFuncs.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(name + " " + m_cfuncp->name()), // + std::forward_as_tuple(nodep, m_cfuncp, m_modp)); } else { - if (m_dpiScopeNames.find(nodep->scopeDpiName()) == m_dpiScopeNames.end()) { - m_dpiScopeNames.emplace(nodep->scopeDpiName(), - // cppcheck-suppress stlFindInsert - ScopeData{nodep, nodep->scopeDpiName(), - nodep->scopePrettyDpiName(), "", timeunit, - "SCOPE_OTHER"}); - } + // Note emplace does not construct when duplicate key + m_dpiScopeNames.emplace( // + std::piecewise_construct, // + std::forward_as_tuple(nodep->scopeDpiName()), // + std::forward_as_tuple(nodep, nodep->scopeDpiName(), nodep->scopePrettyDpiName(), + "", timeunit, "SCOPE_OTHER")); } } void visit(AstVar* nodep) override { nameCheck(nodep); iterateChildrenConst(nodep); - if ((nodep->isSigUserRdPublic() || nodep->isSigUserRWPublic()) && !m_cfuncp) + // Record if public, ignoring locals + if ((nodep->isSigUserRdPublic() || nodep->isSigUserRWPublic()) && !m_cfuncp) { m_modVars.emplace_back(m_modp, nodep); + } } void visit(AstNodeCoverDecl* nodep) override { // Assign numbers to all bins, so we know how big of an array to use @@ -395,13 +403,14 @@ public: void EmitCSyms::emitSymHdr() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + ".h"; + const std::string filename = m_symsFileBase + ".h"; AstCFile* const cfilep = newCFile(filename, true /*slow*/, false /*source*/); V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; setOutputFile(ofilep, cfilep); ofp()->putsHeader(); - puts("// DESCRIPTION: Verilator output: Symbol table internal header\n"); + puts("// DESCR" + "IPTION: Verilator output: Symbol table internal header\n"); puts("//\n"); puts("// Internal details; most calling programs do not need this header,\n"); puts("// unless using verilator public meta comments.\n"); @@ -418,37 +427,36 @@ void EmitCSyms::emitSymHdr() { if (v3Global.opt.usesProfiler()) puts("#include \"verilated_profiler.h\"\n"); puts("\n// INCLUDE MODEL CLASS\n"); - puts("\n#include \"" + EmitCUtil::topClassName() + ".h\"\n"); + puts("\n#include \"" + topClassName() + ".h\"\n"); puts("\n// INCLUDE MODULE CLASSES\n"); - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; - nodep = VN_AS(nodep->nextp(), NodeModule)) { + for (AstNodeModule *nodep = v3Global.rootp()->modulesp(), *nextp; nodep; nodep = nextp) { + nextp = VN_AS(nodep->nextp(), NodeModule); if (VN_IS(nodep, Class)) continue; // Class included earlier putns(nodep, "#include \"" + EmitCUtil::prefixNameProtect(nodep) + ".h\"\n"); } if (v3Global.dpi()) { puts("\n// DPI TYPES for DPI Export callbacks (Internal use)\n"); - std::map types; // Remove duplicates and sort - for (const auto& itr : m_scopeFuncs) { - const AstCFunc* const funcp = itr.second.m_cfuncp; - if (funcp->dpiExportImpl()) { - const string cbtype - = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t"); - const string functype = funcp->rtnTypeVoid() + " (*) (" + cFuncArgs(funcp) + ")"; - types["using " + cbtype + " = " + functype + ";\n"] = 1; - } + std::set types; // Remove duplicates and sort + for (const auto& itpair : m_scopeFuncs) { + const AstCFunc* const funcp = itpair.second.m_cfuncp; + if (!funcp->dpiExportImpl()) continue; + const std::string cbtype + = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t"); + const std::string functype = funcp->rtnTypeVoid() + " (*) (" + cFuncArgs(funcp) + ")"; + types.emplace("using " + cbtype + " = " + functype + ";\n"); } - for (const auto& i : types) puts(i.first); + for (const std::string& type : types) puts(type); } puts("\n// SYMS CLASS (contains all model state)\n"); - puts("class alignas(VL_CACHE_LINE_BYTES) " + EmitCUtil::symClassName() + puts("class alignas(VL_CACHE_LINE_BYTES) " + symClassName() + " final : public VerilatedSyms {\n"); ofp()->putsPrivate(false); // public: puts("// INTERNAL STATE\n"); - puts(EmitCUtil::topClassName() + "* const __Vm_modelp;\n"); + puts(topClassName() + "* const __Vm_modelp;\n"); if (v3Global.needTraceDumper()) { // __Vm_dumperp is local, otherwise we wouldn't know what design's eval() @@ -494,11 +502,11 @@ void EmitCSyms::emitSymHdr() { } puts("\n// MODULE INSTANCE STATE\n"); - for (const auto& i : m_scopes) { - const AstScope* const scopep = i.first; - const AstNodeModule* const modp = i.second; + for (const ScopeModPair& itpair : m_scopes) { + const AstScope* const scopep = itpair.first; + const AstNodeModule* const modp = itpair.second; if (VN_IS(modp, Class)) continue; - const string name = EmitCUtil::prefixNameProtect(modp); + const std::string name = EmitCUtil::prefixNameProtect(modp); ofp()->printf("%-30s ", name.c_str()); putns(scopep, VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); } @@ -507,15 +515,15 @@ void EmitCSyms::emitSymHdr() { puts("\n// COVERAGE\n"); puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); puts(" __Vcoverage["); - puts(cvtToStr(m_coverBins)); + puts(std::to_string(m_coverBins)); puts("];\n"); } if (!m_scopeNames.empty()) { // Scope names puts("\n// SCOPE NAMES\n"); - for (const auto& itr : m_scopeNames) { - putns(itr.second.m_nodep, - "VerilatedScope " + protect("__Vscope_" + itr.second.m_symName) + ";\n"); + for (const auto& itpair : m_scopeNames) { + const ScopeData& sd = itpair.second; + putns(sd.m_nodep, "VerilatedScope " + protect("__Vscope_" + sd.m_symName) + ";\n"); } } @@ -525,12 +533,12 @@ void EmitCSyms::emitSymHdr() { } puts("\n// CONSTRUCTORS\n"); - puts(EmitCUtil::symClassName() + "(VerilatedContext* contextp, const char* namep, " - + EmitCUtil::topClassName() + "* modelp);\n"); - puts("~"s + EmitCUtil::symClassName() + "();\n"); + puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName() + + "* modelp);\n"); + puts("~" + symClassName() + "();\n"); for (const auto& i : m_usesVfinal) { - puts("void " + EmitCUtil::symClassName() + "_" + cvtToStr(i.first) + "("); + puts("void " + symClassName() + "_" + std::to_string(i.first) + "("); if (i.second) puts("int __Vfinal"); puts(");\n"); } @@ -580,39 +588,49 @@ void EmitCSyms::emitSymHdr() { } void EmitCSyms::closeSplit() { - if (!ofp() || ofp() == m_ofpBase) return; + UASSERT(ofp(), "There should be an output file"); + // If we are in the base file, nothign to do + if (ofp() == m_ofpBase) return; + // Close sub-function definition in split file and close the file puts("}\n"); closeOutputFile(); + + // Resume output to the base file + setOutputFile(m_ofpBase, m_ofpBaseFile); } void EmitCSyms::checkSplit(bool usesVfinal) { - if (ofp() - && (!v3Global.opt.outputSplitCFuncs() || m_numStmts < v3Global.opt.outputSplitCFuncs())) { - return; - } + UASSERT(ofp(), "There should be an output file"); + if (!v3Global.opt.outputSplitCFuncs()) return; + if (m_numStmts < v3Global.opt.outputSplitCFuncs()) return; // Splitting file, so using parallel build. v3Global.useParallelBuild(true); - m_numStmts = 0; - const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + "__" - + cvtToStr(++m_funcNum) + ".cpp"; + // If alredy in a split file, close it + if (ofp() != m_ofpBase) closeSplit(); + + // New function umber + const size_t funcNum = ++m_funcNum; + + // Call the sub-function in the base file + puts(symClassName() + "_" + std::to_string(funcNum) + "("); + if (usesVfinal) puts("__Vfinal"); + puts(");\n"); + + // Create new split file + m_usesVfinal[funcNum] = usesVfinal; + const std::string filename = m_symsFileBase + "__" + std::to_string(funcNum) + ".cpp"; AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/); cfilep->support(true); - m_usesVfinal[m_funcNum] = usesVfinal; - closeSplit(); - V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; setOutputFile(ofilep, cfilep); + m_numStmts = 0; - m_ofpBase->puts(EmitCUtil::symClassName() + "_" + cvtToStr(m_funcNum) + "("); - if (usesVfinal) m_ofpBase->puts("__Vfinal"); - m_ofpBase->puts(");\n"); - + // Emit header and open sub function definition in the split file emitSymImpPreamble(); - puts("void " + EmitCUtil::symClassName() + "::" + EmitCUtil::symClassName() + "_" - + cvtToStr(m_funcNum) + "("); + puts("void " + symClassName() + "::" + symClassName() + "_" + std::to_string(funcNum) + "("); if (usesVfinal) puts("int __Vfinal"); puts(") {\n"); } @@ -625,88 +643,80 @@ void EmitCSyms::emitSymImpPreamble() { // Includes puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n"); - puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; - nodep = VN_AS(nodep->nextp(), NodeModule)) { + puts("#include \"" + topClassName() + ".h\"\n"); + for (AstNodeModule *nodep = v3Global.rootp()->modulesp(), *nextp; nodep; nodep = nextp) { + nextp = VN_AS(nodep->nextp(), NodeModule); if (VN_IS(nodep, Class)) continue; // Class included earlier putns(nodep, "#include \"" + EmitCUtil::prefixNameProtect(nodep) + ".h\"\n"); } puts("\n"); // Declarations for DPI Export implementation functions bool needsNewLine = false; - for (const auto& pair : m_scopeFuncs) { - const AstCFunc* const funcp = pair.second.m_cfuncp; + for (const auto& itpair : m_scopeFuncs) { + const AstCFunc* const funcp = itpair.second.m_cfuncp; if (!funcp->dpiExportImpl()) continue; - emitCFuncDecl(funcp, pair.second.m_modp); + emitCFuncDecl(funcp, itpair.second.m_modp); needsNewLine = true; } if (needsNewLine) puts("\n"); } void EmitCSyms::emitScopeHier(bool destroy) { - if (v3Global.opt.vpi()) { - const string verb = destroy ? "Tear down" : "Set up"; - const string method = destroy ? "remove" : "add"; - puts("\n// " + verb + " scope hierarchy\n"); - for (ScopeNames::const_iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); - ++it) { - const string name = it->second.m_prettyName; - if (it->first == "TOP") continue; - const string scopeType = it->second.m_type; - if ((name.find('.') == string::npos) - && (scopeType == "SCOPE_MODULE" || scopeType == "SCOPE_PACKAGE")) { - putns(it->second.m_nodep, "__Vhier." + method + "(0, &" - + protect("__Vscope_" + it->second.m_symName) - + ");\n"); - } - } + if (!v3Global.opt.vpi()) return; - for (auto it = m_vpiScopeHierarchy.cbegin(); it != m_vpiScopeHierarchy.cend(); ++it) { - for (ScopeNameList::const_iterator lit = it->second.begin(); lit != it->second.end(); - ++lit) { - const string fromname = scopeSymString(it->first); - const string toname = scopeSymString(*lit); - const auto from = vlstd::as_const(m_scopeNames).find(fromname); - const auto to = vlstd::as_const(m_scopeNames).find(toname); - UASSERT(from != m_scopeNames.end(), fromname + " not in m_scopeNames"); - UASSERT(to != m_scopeNames.end(), toname + " not in m_scopeNames"); - puts("__Vhier." + method + "("); - puts("&" + protect("__Vscope_" + from->second.m_symName) + ", "); - puts("&" + protect("__Vscope_" + to->second.m_symName) + ");\n"); - } - } - puts("\n"); + const std::string verb = destroy ? "Tear down" : "Set up"; + const std::string method = destroy ? "remove" : "add"; + puts("\n// " + verb + " scope hierarchy\n"); + for (const auto& itpair : m_scopeNames) { + if (itpair.first == "TOP") continue; + const ScopeData& sd = itpair.second; + const std::string& name = sd.m_prettyName; + const std::string& scopeType = sd.m_type; + if (name.find('.') != string::npos) continue; + if (scopeType != "SCOPE_MODULE" && scopeType != "SCOPE_PACKAGE") continue; + putns(sd.m_nodep, + "__Vhier." + method + "(0, &" + protect("__Vscope_" + sd.m_symName) + ");\n"); } + + for (const auto& itpair : m_vpiScopeHierarchy) { + const std::string fromname = scopeSymString(itpair.first); + const ScopeData& from = m_scopeNames.at(fromname); + for (const std::string& name : itpair.second) { + const std::string toname = scopeSymString(name); + const ScopeData& to = m_scopeNames.at(toname); + puts("__Vhier." + method + "("); + puts("&" + protect("__Vscope_" + from.m_symName) + ", "); + puts("&" + protect("__Vscope_" + to.m_symName) + ");\n"); + } + } + puts("\n"); } void EmitCSyms::emitSymImp() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::symClassName() + ".cpp"; - AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/); - cfilep->support(true); + const std::string filename = m_symsFileBase + ".cpp"; + m_ofpBaseFile = newCFile(filename, true /*slow*/, true /*source*/); + m_ofpBaseFile->support(true); + m_ofpBase = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; + setOutputFile(m_ofpBase, m_ofpBaseFile); - V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; - setOutputFile(ofilep, cfilep); - - m_ofpBase = ofp(); emitSymImpPreamble(); if (v3Global.opt.savable()) { for (const bool& de : {false, true}) { - const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - const string funcname = de ? "__Vdeserialize" : "__Vserialize"; - const string op = de ? ">>" : "<<"; + const std::string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + const std::string funcname = protect(de ? "__Vdeserialize" : "__Vserialize"); + const std::string op = de ? ">>" : "<<"; // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void " + EmitCUtil::symClassName() + "::" + protect(funcname) + "(" + classname - + "& os) {\n"); + puts("void " + symClassName() + "::" + funcname + "(" + classname + "& os) {\n"); puts("// Internal state\n"); if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n"); puts("os " + op + " __Vm_didInit;\n"); puts("// Module instance state\n"); - for (const auto& pair : m_scopes) { - const AstScope* const scopep = pair.first; + for (const ScopeModPair& itpair : m_scopes) { + const AstScope* const scopep = itpair.first; puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." - + protect(funcname) + "(os);\n"); + + funcname + "(os);\n"); } puts("}\n"); } @@ -716,7 +726,7 @@ void EmitCSyms::emitSymImp() { puts("// FUNCTIONS\n"); // Destructor - puts(EmitCUtil::symClassName() + "::~" + EmitCUtil::symClassName() + "()\n"); + puts(symClassName() + "::~" + symClassName() + "()\n"); puts("{\n"); emitScopeHier(true); if (v3Global.needTraceDumper()) { @@ -725,21 +735,21 @@ void EmitCSyms::emitSymImp() { puts("#endif // VM_TRACE\n"); } if (v3Global.opt.profPgo()) { - puts("_vm_pgoProfiler.write(\"" + EmitCUtil::topClassName() + puts("_vm_pgoProfiler.write(\"" + topClassName() + "\", _vm_contextp__->profVltFilename());\n"); } puts("}\n"); if (v3Global.needTraceDumper()) { if (!optSystemC()) { - puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDump() {\n"); + puts("\nvoid " + symClassName() + "::_traceDump() {\n"); // Caller checked for __Vm_dumperp non-nullptr puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumperp->dump(VL_TIME_Q());\n"); puts("}\n"); } - puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDumpOpen() {\n"); + puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n"); puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n"); puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); @@ -750,7 +760,7 @@ void EmitCSyms::emitSymImp() { puts("}\n"); puts("}\n"); - puts("\nvoid " + EmitCUtil::symClassName() + "::_traceDumpClose() {\n"); + puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n"); puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); puts("__Vm_dumping = false;\n"); puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n"); @@ -759,35 +769,13 @@ void EmitCSyms::emitSymImp() { puts("\n"); // Constructor - puts(EmitCUtil::symClassName() + "::" + EmitCUtil::symClassName() - + "(VerilatedContext* contextp, const char* namep, " + EmitCUtil::topClassName() - + "* modelp)\n"); + puts(symClassName() + "::" + symClassName() + + "(VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp)\n"); puts(" : VerilatedSyms{contextp}\n"); puts(" // Setup internal state of the Syms class\n"); puts(" , __Vm_modelp{modelp}\n"); if (v3Global.opt.mtasks()) { - // TODO -- For now each model creates its own ThreadPool here, - // and deletes it in the destructor. If A and B are each top level - // modules, each creates a separate thread pool. This allows - // A.eval() and B.eval() to run concurrently without any - // interference -- so long as the physical machine has enough cores - // to support both pools and all testbench threads. - // - // In the future, we might want to let the client provide a - // threadpool to the constructor. This would allow two or more - // models to share a single threadpool. - // - // For example: suppose models A and B are each compiled to run on - // 4 threads. The client might create a single thread pool with 3 - // threads and pass it to both models. If the client can ensure that - // A.eval() and B.eval() do NOT run concurrently, there will be no - // contention for the threads. This mode is missing for now. (Is - // there demand for such a setup?) - // - // Note we create N-1 threads in the thread pool. The thread - // that calls eval() becomes the final Nth thread for the - // duration of the eval call. puts(" , __Vm_threadPoolp{static_cast(contextp->threadPoolp())}\n"); } @@ -801,9 +789,9 @@ void EmitCSyms::emitSymImp() { } puts(" // Setup module instances\n"); - for (const auto& i : m_scopes) { - const AstScope* const scopep = i.first; - const AstNodeModule* const modp = i.second; + for (const ScopeModPair& itpair : m_scopes) { + const AstScope* const scopep = itpair.first; + const AstNodeModule* const modp = itpair.second; puts(" , "); putns(scopep, protect(scopep->nameDotless())); puts("{this"); @@ -825,7 +813,7 @@ void EmitCSyms::emitSymImp() { uint64_t stackSize = V3StackCount::count(v3Global.rootp()); if (v3Global.opt.debugStackCheck()) stackSize += 1024 * 1024 * 1024; V3Stats::addStat("Size prediction, Stack (bytes)", stackSize); - puts("Verilated::stackCheck(" + cvtToStr(stackSize) + ");\n"); + puts("Verilated::stackCheck(" + std::to_string(stackSize) + ");\n"); // TODO: 'm_statVarScopeBytes' is always 0, AstVarScope doesn't reach here (V3Descope) V3Stats::addStat("Size prediction, Heap, from Var Scopes (bytes)", m_statVarScopeBytes); V3Stats::addStat(V3Stats::STAT_MODEL_SIZE, stackSize + m_statVarScopeBytes); @@ -840,8 +828,8 @@ void EmitCSyms::emitSymImp() { v3Global.rootp()->topModulep()->foreach([&](const AstExecGraph* execGraphp) { for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) { const ExecMTask& mt = static_cast(vtx); - puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mt.id()) + ", \"" + mt.hashName() - + "\");\n"); + puts("_vm_pgoProfiler.addCounter(" + std::to_string(mt.id()) + ", \"" + + mt.hashName() + "\");\n"); } }); } @@ -850,12 +838,12 @@ void EmitCSyms::emitSymImp() { puts("// Configure time unit / time precision\n"); if (!v3Global.rootp()->timeunit().isNone()) { puts("_vm_contextp__->timeunit("); - puts(cvtToStr(v3Global.rootp()->timeunit().powerOfTen())); + puts(std::to_string(v3Global.rootp()->timeunit().powerOfTen())); puts(");\n"); } if (!v3Global.rootp()->timeprecision().isNone()) { puts("_vm_contextp__->timeprecision("); - puts(cvtToStr(v3Global.rootp()->timeprecision().powerOfTen())); + puts(std::to_string(v3Global.rootp()->timeprecision().powerOfTen())); puts(");\n"); } @@ -863,27 +851,27 @@ void EmitCSyms::emitSymImp() { for (const auto& i : m_scopes) { const AstScope* const scopep = i.first; const AstNodeModule* const modp = i.second; - if (const AstScope* const aboveScopep = scopep->aboveScopep()) { - checkSplit(false); - const string protName = VIdProtect::protectWordsIf(scopep->name(), scopep->protect()); - if (VN_IS(modp, ClassPackage)) { - // ClassPackage modules seem to be a bit out of place, so hard code... - putns(scopep, "TOP"); - } else { - putns(scopep, - VIdProtect::protectIf(aboveScopep->nameDotless(), aboveScopep->protect())); - } - puts("."); - puts(protName.substr(protName.rfind('.') + 1)); - puts(" = &"); - puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); - ++m_numStmts; + const AstScope* const abovep = scopep->aboveScopep(); + if (!abovep) continue; + + checkSplit(false); + const std::string protName = VIdProtect::protectWordsIf(scopep->name(), scopep->protect()); + if (VN_IS(modp, ClassPackage)) { + // ClassPackage modules seem to be a bit out of place, so hard code... + putns(scopep, "TOP"); + } else { + putns(scopep, VIdProtect::protectIf(abovep->nameDotless(), abovep->protect())); } + puts("."); + puts(protName.substr(protName.rfind('.') + 1)); + puts(" = &"); + puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); + ++m_numStmts; } puts("// Setup each module's pointer back to symbol table (for public functions)\n"); - for (const auto& i : m_scopes) { - AstScope* const scopep = i.first; + for (const ScopeModPair& i : m_scopes) { + const AstScope* const scopep = i.first; AstNodeModule* const modp = i.second; checkSplit(false); // first is used by AstCoverDecl's call to __vlCoverInsert @@ -896,18 +884,18 @@ void EmitCSyms::emitSymImp() { if (!m_scopeNames.empty()) { // Setup scope names puts("// Setup scopes\n"); - for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + for (const auto& itpair : m_scopeNames) { checkSplit(false); - putns(it->second.m_nodep, - protect("__Vscope_" + it->second.m_symName) + ".configure(this, name(), "); - putsQuoted(VIdProtect::protectWordsIf(it->second.m_prettyName, true)); + const ScopeData& sd = itpair.second; + putns(sd.m_nodep, protect("__Vscope_" + sd.m_symName) + ".configure(this, name(), "); + putsQuoted(VIdProtect::protectWordsIf(sd.m_prettyName, true)); puts(", "); - putsQuoted(protect(scopeDecodeIdentifier(it->second.m_prettyName))); + putsQuoted(protect(scopeDecodeIdentifier(sd.m_prettyName))); puts(", "); - putsQuoted(it->second.m_defName); + putsQuoted(sd.m_defName); puts(", "); - puts(cvtToStr(it->second.m_timeunit)); - puts(", VerilatedScope::" + it->second.m_type + ");\n"); + puts(std::to_string(sd.m_timeunit)); + puts(", VerilatedScope::" + sd.m_type + ");\n"); ++m_numStmts; } } @@ -918,44 +906,49 @@ void EmitCSyms::emitSymImp() { closeSplit(); if (v3Global.dpi()) { - m_ofpBase->puts("// Setup export functions\n"); - m_ofpBase->puts("for (int __Vfinal = 0; __Vfinal < 2; ++__Vfinal) {\n"); - for (auto it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { - AstScopeName* const scopep = it->second.m_scopep; - AstCFunc* const funcp = it->second.m_cfuncp; - AstNodeModule* const modp = it->second.m_modp; - if (funcp->dpiExportImpl()) { - checkSplit(true); - putns(scopep, - protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); - putsQuoted(funcp->cname()); // Not protected - user asked for import/export - puts(", (void*)(&"); - puts(EmitCUtil::prefixNameProtect(modp)); - puts("__"); - puts(funcp->nameProtect()); - puts("));\n"); - ++m_numStmts; - } + + puts("// Setup export functions\n"); + puts("for (int __Vfinal = 0; __Vfinal < 2; ++__Vfinal) {\n"); + for (const auto& itpair : m_scopeFuncs) { + const ScopeFuncData& sfd = itpair.second; + const AstScopeName* const scopep = sfd.m_scopep; + const AstCFunc* const funcp = sfd.m_cfuncp; + const AstNodeModule* const modp = sfd.m_modp; + + if (!funcp->dpiExportImpl()) continue; + + checkSplit(true); + putns(scopep, + protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); + putsQuoted(funcp->cname()); // Not protected - user asked for import/export + puts(", (void*)(&"); + puts(EmitCUtil::prefixNameProtect(modp)); + puts("__"); + puts(funcp->nameProtect()); + puts("));\n"); + ++m_numStmts; } // It would be less code if each module inserted its own variables. // Someday. For now public isn't common. - for (auto it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) { + for (const auto& itpair : m_scopeVars) { checkSplit(true); - AstScope* const scopep = it->second.m_scopep; - AstVar* const varp = it->second.m_varp; + const ScopeVarData& svd = itpair.second; + + const AstScope* const scopep = svd.m_scopep; + const AstVar* const varp = svd.m_varp; int pdim = 0; int udim = 0; - string bounds; - if (AstBasicDType* const basicp = varp->basicp()) { + std::string bounds; + if (const AstBasicDType* const basicp = varp->basicp()) { // Range is always first, it's not in "C" order for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) { - dtypep - = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + // Skip AstRefDType/AstTypedef, or return same node + dtypep = dtypep->skipRefp(); if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) { bounds += " ,"; - bounds += cvtToStr(adtypep->left()); + bounds += std::to_string(adtypep->left()); bounds += ","; - bounds += cvtToStr(adtypep->right()); + bounds += std::to_string(adtypep->right()); if (VN_IS(dtypep, PackArrayDType)) pdim++; else @@ -964,9 +957,9 @@ void EmitCSyms::emitSymImp() { } else { if (basicp->isRanged()) { bounds += " ,"; - bounds += cvtToStr(basicp->left()); + bounds += std::to_string(basicp->left()); bounds += ","; - bounds += cvtToStr(basicp->right()); + bounds += std::to_string(basicp->right()); pdim++; } break; // AstBasicDType - nothing below, 1 @@ -974,13 +967,13 @@ void EmitCSyms::emitSymImp() { } } - putns(scopep, protect("__Vscope_" + it->second.m_scopeName)); + putns(scopep, protect("__Vscope_" + svd.m_scopeName)); putns(varp, ".varInsert(__Vfinal,"); - putsQuoted(protect(it->second.m_varBasePretty)); + putsQuoted(protect(svd.m_varBasePretty)); - std::string varName; - varName += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "."; - varName += protect(varp->name()); + const std::string varName + = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + + protect(varp->name()); if (varp->isParam()) { if (varp->vlEnumType() == "VLVT_STRING" @@ -1005,20 +998,20 @@ void EmitCSyms::emitSymImp() { puts(","); puts(varp->vlEnumDir()); // VLVD_IN etc puts(","); - puts(cvtToStr(udim)); + puts(std::to_string(udim)); puts(","); - puts(cvtToStr(pdim)); + puts(std::to_string(pdim)); puts(bounds); puts(");\n"); ++m_numStmts; } - m_ofpBase->puts("}\n"); + + closeSplit(); + puts("}\n"); } - m_ofpBase->puts("}\n"); + puts("}\n"); - closeSplit(); - setOutputFile(nullptr); VL_DO_CLEAR(delete m_ofpBase, m_ofpBase = nullptr); } @@ -1026,7 +1019,7 @@ void EmitCSyms::emitSymImp() { void EmitCSyms::emitDpiHdr() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi.h"; + const std::string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h"; AstCFile* const cfilep = newCFile(filename, false /*slow*/, false /*source*/); cfilep->support(true); V3OutCFile hf{filename}; @@ -1052,22 +1045,19 @@ void EmitCSyms::emitDpiHdr() { int firstExp = 0; int firstImp = 0; - for (AstCFunc* nodep : m_dpis) { + for (const AstCFunc* const nodep : m_dpis) { + if (!nodep->dpiExportDispatcher() && !nodep->dpiImportPrototype()) continue; + + const std::string sourceLoc = VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()); if (nodep->dpiExportDispatcher()) { if (!firstExp++) puts("\n// DPI EXPORTS\n"); - putsDecoration( - nodep, "// DPI export" - + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); - putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" - + cFuncArgs(nodep) + ");\n"); - } else if (nodep->dpiImportPrototype()) { + putsDecoration(nodep, "// DPI export" + sourceLoc + "\n"); + } else { if (!firstImp++) puts("\n// DPI IMPORTS\n"); - putsDecoration( - nodep, "// DPI import" - + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) + "\n"); - putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" - + cFuncArgs(nodep) + ");\n"); + putsDecoration(nodep, "// DPI import" + sourceLoc + "\n"); } + putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "(" + + cFuncArgs(nodep) + ");\n"); } puts("\n"); @@ -1083,7 +1073,7 @@ void EmitCSyms::emitDpiHdr() { void EmitCSyms::emitDpiImp() { UINFO(6, __FUNCTION__ << ": "); - const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi.cpp"; + const std::string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp"; AstCFile* const cfilep = newCFile(filename, false /*slow*/, true /*source*/); cfilep->support(true); V3OutCFile hf(filename); @@ -1098,41 +1088,42 @@ void EmitCSyms::emitDpiImp() { puts("// function names, you will get multiple definition link errors from here.\n"); puts("// This is an unfortunate result of the DPI specification.\n"); puts("// To solve this, either\n"); - puts("// 1. Call " + EmitCUtil::topClassName() + "::{export_function} instead,\n"); + puts("// 1. Call " + topClassName() + "::{export_function} instead,\n"); puts("// and do not even bother to compile this file\n"); puts("// or 2. Compile all __Dpi.cpp files in the same compiler run,\n"); puts("// and #ifdefs already inserted here will sort everything out.\n"); puts("\n"); - puts("#include \"" + EmitCUtil::topClassName() + "__Dpi.h\"\n"); - puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n"); + puts("#include \"" + topClassName() + "__Dpi.h\"\n"); + puts("#include \"" + topClassName() + ".h\"\n"); puts("\n"); - for (AstCFunc* nodep : m_dpis) { - if (nodep->dpiExportDispatcher()) { - // Prevent multi-definition if used by multiple models - puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n"); - puts("#define VL_DPIDECL_" + nodep->name() + "_\n"); - putns(nodep, - nodep->rtnTypeVoid() + " " + nodep->name() + "(" + cFuncArgs(nodep) + ") {\n"); - puts("// DPI export" + VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()) - + "\n"); - putns(nodep, "return " + EmitCUtil::topClassName() + "::" + nodep->name() + "("); - string comma; - for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) { - if (const AstVar* const portp = VN_CAST(stmtp, Var)) { - if (portp->isIO() && !portp->isFuncReturn()) { - puts(comma); - comma = ", "; - putns(portp, portp->name()); - } + for (const AstCFunc* const nodep : m_dpis) { + if (!nodep->dpiExportDispatcher()) continue; + + const std::string name = nodep->name(); + const std::string sourceLoc = VIdProtect::ifNoProtect(" at " + nodep->fileline()->ascii()); + + // Prevent multi-definition if used by multiple models + puts("#ifndef VL_DPIDECL_" + name + "_\n"); + puts("#define VL_DPIDECL_" + name + "_\n"); + putns(nodep, nodep->rtnTypeVoid() + " " + name + "(" + cFuncArgs(nodep) + ") {\n"); + puts("// DPI export" + sourceLoc + "\n"); + putns(nodep, "return " + topClassName() + "::" + name + "("); + std::string comma; + for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) { + if (const AstVar* const portp = VN_CAST(stmtp, Var)) { + if (portp->isIO() && !portp->isFuncReturn()) { + puts(comma); + comma = ", "; + putns(portp, portp->name()); } } - puts(");\n"); - puts("}\n"); - puts("#endif\n"); - puts("\n"); } + puts(");\n"); + puts("}\n"); + puts("#endif\n"); + puts("\n"); } setOutputFile(nullptr); } diff --git a/src/cppcheck-suppressions.txt b/src/cppcheck-suppressions.txt index 431056957..80d510457 100644 --- a/src/cppcheck-suppressions.txt +++ b/src/cppcheck-suppressions.txt @@ -51,7 +51,6 @@ constVariablePointer:src/V3EmitCFunc.h constParameterPointer:src/V3EmitCImp.cpp constParameterPointer:src/V3EmitCModel.cpp constVariablePointer:src/V3EmitCModel.cpp -constVariablePointer:src/V3EmitCSyms.cpp constVariablePointer:src/V3EmitV.cpp constParameterReference:src/V3Error.cpp constParameterPointer:src/V3ExecGraph.cpp From e2f5854088ffcccb63a5438b9de0b93531e75774 Mon Sep 17 00:00:00 2001 From: Jens Yuechao Liu Date: Wed, 5 Nov 2025 11:48:22 +0100 Subject: [PATCH 014/246] Fix slice memory overflow on large output arrays (#6636) (#6638) --- docs/CONTRIBUTORS | 1 + docs/guide/exe_verilator.rst | 5 +++++ src/V3Options.cpp | 5 ++++- src/V3Options.h | 2 ++ src/V3Slice.cpp | 11 +++++++++- test_regress/t/t_opt_slice_element_limit.py | 20 ++++++++++++++++++ test_regress/t/t_opt_slice_element_limit.v | 20 ++++++++++++++++++ .../t/t_opt_slice_element_limit_allow_all.py | 20 ++++++++++++++++++ .../t/t_opt_slice_element_limit_bad.out | 2 ++ .../t/t_opt_slice_element_limit_bad.py | 21 +++++++++++++++++++ .../t/t_opt_slice_element_limit_default.py | 20 ++++++++++++++++++ 11 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 test_regress/t/t_opt_slice_element_limit.py create mode 100644 test_regress/t/t_opt_slice_element_limit.v create mode 100644 test_regress/t/t_opt_slice_element_limit_allow_all.py create mode 100644 test_regress/t/t_opt_slice_element_limit_bad.out create mode 100644 test_regress/t/t_opt_slice_element_limit_bad.py create mode 100644 test_regress/t/t_opt_slice_element_limit_default.py diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 968b95e1b..e5f399df8 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -105,6 +105,7 @@ Jamey Hicks Jamie Iles Jan Van Winkel Jean Berniolles +Jens Yuechao Liu Jeremy Bennett Jesse Taube Jevin Sweval diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 2f11d50cb..4df0a1e1a 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -719,6 +719,11 @@ Summary: automatically. Variables explicitly annotated with :option:`/*verilator&32;split_var*/` are still split. +.. option:: --fslice-element-limit + + Rarely needed. Set the maximum array size (number of elements) + for slice optimization to avoid excessive memory usage. + .. option:: -future0