From eb5676366bd0451d667cdc7c83e224e64fe6dcee Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Apr 2025 13:06:34 -0400 Subject: [PATCH 001/211] devel release --- CMakeLists.txt | 2 +- Changes | 6 ++++++ configure.ac | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 705e8e4cf..8e17aece0 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.036 + VERSION 5.037 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index 90604dccc..69deeb0ef 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,12 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 5.037 devel +========================== + +**Other:** + + Verilator 5.036 2025-04-27 ========================== diff --git a/configure.ac b/configure.ac index e6c608eee..7ea13e2cd 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.036 2025-04-27], +AC_INIT([Verilator],[5.037 devel], [https://verilator.org], [verilator],[https://verilator.org]) From cb1661f9d05e1354a69810357aa5a4c2d04c9e5e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Apr 2025 14:17:24 -0400 Subject: [PATCH 002/211] Internals: Cleanups (from parse branch). No functional change intended. --- src/V3AstNodes.cpp | 4 ++-- src/V3CCtors.cpp | 2 +- src/V3Clean.cpp | 2 +- src/V3Clock.cpp | 7 ++++--- src/V3Depth.cpp | 8 +++++++- src/V3DepthBlock.cpp | 6 +++--- src/V3LifePost.cpp | 1 + src/V3LinkCells.cpp | 2 +- src/V3Sampled.cpp | 3 ++- src/V3SenTree.h | 2 +- src/V3Subst.cpp | 4 ++-- 11 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 9ad85e30a..0353a1317 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1861,10 +1861,10 @@ void AstIfaceRefDType::dump(std::ostream& str) const { if (ifaceName() != "") str << " if=" << ifaceName(); if (modportName() != "") str << " mp=" << modportName(); if (cellp()) { - str << " -> "; + str << " c-> "; cellp()->dump(str); } else if (ifacep()) { - str << " -> "; + str << " i-> "; ifacep()->dump(str); } else { str << " -> UNLINKED"; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 101841229..92728f867 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -132,7 +132,7 @@ private: class CCtorsVisitor final : public VNVisitor { // NODE STATE - // STATE + // STATE - for current visit position (use VL_RESTORER) AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current function V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index ca3a16246..970675062 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -45,7 +45,7 @@ class CleanVisitor final : public VNVisitor { // TYPES enum CleanState : uint8_t { CS_UNKNOWN, CS_CLEAN, CS_DIRTY }; - // STATE + // STATE - for current visit position (use VL_RESTORER) const AstNodeModule* m_modp = nullptr; // METHODS diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index a813f7b80..ec6d8bf21 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -68,8 +68,9 @@ public: class ClockVisitor final : public VNVisitor { // NODE STATE + // STATE - AstCFunc* m_evalp = nullptr; // The '_eval' function + AstCFunc* const m_evalp = nullptr; // The '_eval' function AstSenTree* m_lastSenp = nullptr; // Last sensitivity match, so we can detect duplicates. AstIf* m_lastIfp = nullptr; // Last sensitivity if active to add more under @@ -174,8 +175,8 @@ class ClockVisitor final : public VNVisitor { public: // CONSTRUCTORS - explicit ClockVisitor(AstNetlist* netlistp) { - m_evalp = netlistp->evalp(); + explicit ClockVisitor(AstNetlist* netlistp) + : m_evalp{netlistp->evalp()} { // Simplify all SenTrees for (AstSenTree* senTreep = netlistp->topScopep()->senTreesp(); senTreep; senTreep = VN_AS(senTreep->nextp(), SenTree)) { diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index d28db344d..4d7803fef 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -36,7 +36,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; class DepthVisitor final : public VNVisitor { // NODE STATE - // STATE + // 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 @@ -71,6 +71,8 @@ class DepthVisitor final : public VNVisitor { 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; @@ -81,6 +83,8 @@ class DepthVisitor final : public VNVisitor { 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; @@ -90,6 +94,8 @@ class DepthVisitor final : public VNVisitor { } void visitStmt(AstNodeStmt* nodep) { VL_RESTORER(m_stmtp); + VL_RESTORER(m_depth); + VL_RESTORER(m_maxdepth); m_stmtp = nodep; m_depth = 0; m_maxdepth = 0; diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index 21b30e411..8c31fc7df 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -33,7 +33,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; class DepthBlockVisitor final : public VNVisitor { // NODE STATE - // STATE + // STATE - for current visit position (use VL_RESTORER) const AstNodeModule* m_modp = nullptr; // Current module const AstCFunc* m_cfuncp = nullptr; // Current function int m_depth = 0; // How deep in an expression @@ -85,7 +85,7 @@ class DepthBlockVisitor final : public VNVisitor { void visit(AstStmtExpr* nodep) override {} // Stop recursion after introducing new function void visit(AstJumpBlock*) override {} // Stop recursion as can't break up across a jump void visit(AstNodeStmt* nodep) override { - m_depth++; + ++m_depth; if (m_depth > v3Global.opt.compLimitBlocks()) { // Already done UINFO(4, "DeepBlocks " << m_depth << " " << nodep << endl); const AstNode* const backp = nodep->backp(); // Only for debug @@ -97,7 +97,7 @@ class DepthBlockVisitor final : public VNVisitor { } else { iterateChildren(nodep); } - m_depth--; + --m_depth; } void visit(AstNodeExpr*) override {} // Accelerate diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index e5b9851b1..2a8f8792d 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -47,6 +47,7 @@ class LifePostElimVisitor final : public VNVisitor { // INPUT: // AstVarScope::user4p() -> AstVarScope*, If set, replace this // varscope with specified new one + // STATE // VISITORS diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 3bd67bdef..3542be191 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -125,7 +125,7 @@ class LinkCellsVisitor final : public VNVisitor { return nodep->user1u().toGraphVertex(); } void newEdge(V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cuttable) { - V3GraphEdge* const edgep = new V3GraphEdge{&m_graph, fromp, top, weight, cuttable}; + const V3GraphEdge* const edgep = new V3GraphEdge{&m_graph, fromp, top, weight, cuttable}; UINFO(9, " newEdge " << edgep << " " << fromp->name() << " -> " << top->name() << endl); } diff --git a/src/V3Sampled.cpp b/src/V3Sampled.cpp index 8058fa683..45fdbfafc 100644 --- a/src/V3Sampled.cpp +++ b/src/V3Sampled.cpp @@ -35,7 +35,8 @@ class SampledVisitor final : public VNVisitor { // AstVarScope::user1() -> AstVarScope*. The VarScope that stores sampled value // AstVarRef::user1() -> bool. Whether already converted const VNUser1InUse m_user1InUse; - // STATE + + // STATE - for current visit position (use VL_RESTORER) AstScope* m_scopep = nullptr; // Current scope bool m_inSampled = false; // True inside a sampled expression diff --git a/src/V3SenTree.h b/src/V3SenTree.h index 77ff33514..2556b1138 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -32,7 +32,7 @@ // And provide functions to find/add a new one class SenTreeFinder final { - // STATE + // STATE - across all visitors AstTopScope* const m_topScopep; // Top scope to add global SenTrees to std::unordered_set> m_trees; // Set of global SenTrees AstSenTree* m_combop = nullptr; // The unique combinational domain SenTree diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index f5745a277..08af8a416 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -170,8 +170,8 @@ public: class SubstUseVisitor final : public VNVisitorConst { // NODE STATE // See SubstVisitor - // - // STATE + + // STATE - across all visitors const int m_origStep; // Step number where subst was recorded bool m_ok = true; // No misassignments found From 4c4a39a1158929bfd150371c5ec1b915d1c90a27 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Apr 2025 16:52:26 -0400 Subject: [PATCH 003/211] Internals: Refactor V3LinkDot dump and comments. No functional change intended. --- src/V3LinkDot.cpp | 160 ++++++++++++++++++++++------------------------ src/V3LinkDot.h | 1 + 2 files changed, 79 insertions(+), 82 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 825da71d1..ba749b975 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -153,7 +153,7 @@ private: static LinkDotState* s_errorThisp; // Last self, for error reporting only // MEMBERS - VSymGraph m_syms; // Symbol table + VSymGraph m_syms; // Symbol table by hierarchy VSymEnt* m_dunitEntp = nullptr; // $unit entry std::multimap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name @@ -847,7 +847,7 @@ class LinkDotFindVisitor final : public VNVisitor { } // VISITORS - void visit(AstNetlist* nodep) override { + void visit(AstNetlist* nodep) override { // FindVisitor:: // Process $unit or other packages // Not needed - dotted references not allowed from inside packages // for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); @@ -922,9 +922,9 @@ class LinkDotFindVisitor final : public VNVisitor { m_curSymp = m_modSymp = nullptr; } } - void visit(AstTypeTable*) override {} - void visit(AstConstPool*) override {} - void visit(AstNodeModule* nodep) override { + void visit(AstTypeTable*) override {} // FindVisitor:: + void visit(AstConstPool*) override {} // FindVisitor:: + void visit(AstNodeModule* nodep) override { // FindVisitor:: // Called on top module from Netlist, other modules from the cell creating them, // and packages UINFO(8, " " << nodep << endl); @@ -1007,7 +1007,7 @@ class LinkDotFindVisitor final : public VNVisitor { } } - void visit(AstClass* nodep) override { + void visit(AstClass* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Class not under module/package/$unit"); UINFO(8, " " << nodep << endl); VL_RESTORER(m_scope); @@ -1042,18 +1042,18 @@ class LinkDotFindVisitor final : public VNVisitor { if (!m_explicitNew && m_statep->forPrimary()) makeImplicitNew(nodep); } } - void visit(AstClassOrPackageRef* nodep) override { + void visit(AstClassOrPackageRef* nodep) override { // FindVisitor:: if (!nodep->classOrPackageNodep() && nodep->name() == "$unit") { nodep->classOrPackageNodep(v3Global.rootp()->dollarUnitPkgAddp()); } iterateChildren(nodep); } - void visit(AstScope* nodep) override { + void visit(AstScope* nodep) override { // FindVisitor:: UASSERT_OBJ(m_statep->forScopeCreation(), nodep, "Scopes should only exist right after V3Scope"); // Ignored. Processed in next step } - void visit(AstCell* nodep) override { + void visit(AstCell* nodep) override { // FindVisitor:: UINFO(5, " CELL under " << m_scope << " is " << nodep << endl); // Process XREFs/etc inside pins if (nodep->recursive() && m_inRecursion) return; @@ -1089,7 +1089,7 @@ class LinkDotFindVisitor final : public VNVisitor { if (nodep->modp()) iterate(nodep->modp()); } } - void visit(AstCellInline* nodep) override { + void visit(AstCellInline* nodep) override { // FindVisitor:: UINFO(5, " CELLINLINE under " << m_scope << " is " << nodep << endl); VSymEnt* aboveSymp = m_curSymp; // If baz__DOT__foo__DOT__bar, we need to find baz__DOT__foo and add bar to it. @@ -1110,11 +1110,11 @@ class LinkDotFindVisitor final : public VNVisitor { m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name()); } } - void visit(AstDefParam* nodep) override { + void visit(AstDefParam* nodep) override { // FindVisitor:: nodep->user1p(m_curSymp); iterateChildren(nodep); } - void visit(AstNodeBlock* nodep) override { + void visit(AstNodeBlock* nodep) override { // FindVisitor:: UINFO(5, " " << nodep << endl); if (nodep->name() == "" && nodep->unnamed()) { // Unnamed blocks are only important when they contain var @@ -1153,7 +1153,7 @@ class LinkDotFindVisitor final : public VNVisitor { } } } - void visit(AstNodeFTask* nodep) override { + void visit(AstNodeFTask* nodep) override { // FindVisitor:: // NodeTask: Remember its name for later resolution UINFO(5, " " << nodep << endl); UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Function/Task not under module?"); @@ -1271,7 +1271,7 @@ class LinkDotFindVisitor final : public VNVisitor { iterateChildren(nodep); } } - void visit(AstClocking* nodep) override { + void visit(AstClocking* nodep) override { // FindVisitor:: VL_RESTORER(m_clockingp); m_clockingp = nodep; iterate(nodep->sensesp()); @@ -1295,7 +1295,7 @@ class LinkDotFindVisitor final : public VNVisitor { iterateAndNextNull(nodep->itemsp()); } } - void visit(AstClockingItem* nodep) override { + void visit(AstClockingItem* nodep) override { // FindVisitor:: if (nodep->varp()) { if (m_curSymp->nodep() == m_clockingp) iterate(nodep->varp()); return; @@ -1327,7 +1327,7 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->varp(newvarp); iterate(nodep->exprp()); } - void visit(AstConstraint* nodep) override { + void visit(AstConstraint* nodep) override { // FindVisitor:: VL_RESTORER(m_curSymp); // Change to appropriate package if extern declaration (vs definition) VSymEnt* upSymp = m_curSymp; @@ -1360,7 +1360,7 @@ class LinkDotFindVisitor final : public VNVisitor { m_curSymp = m_statep->insertBlock(upSymp, name, nodep, m_classOrPackagep); iterateChildren(nodep); } - void visit(AstVar* nodep) override { + void visit(AstVar* nodep) override { // FindVisitor:: // Var: Remember its name for later resolution UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?"); iterateChildren(nodep); @@ -1513,18 +1513,18 @@ class LinkDotFindVisitor final : public VNVisitor { } } } - void visit(AstTypedef* nodep) override { + void visit(AstTypedef* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Typedef not under module/package/$unit"); iterateChildren(nodep); m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); } - void visit(AstTypedefFwd* nodep) override { + void visit(AstTypedefFwd* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Typedef not under module/package/$unit"); iterateChildren(nodep); // No need to insert, only the real typedef matters, but need to track for errors nodep->user1p(m_curSymp); } - void visit(AstParamTypeDType* nodep) override { + void visit(AstParamTypeDType* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Parameter type not under module/package/$unit"); // Replace missing param types with provided hierarchical type params. @@ -1558,11 +1558,11 @@ class LinkDotFindVisitor final : public VNVisitor { symp->exported(false); } } - void visit(AstCFunc* nodep) override { + void visit(AstCFunc* nodep) override { // FindVisitor:: // For dotted resolution, ignore all AstVars under functions, otherwise shouldn't exist UASSERT_OBJ(!m_statep->forScopeCreation(), nodep, "No CFuncs expected in tree yet"); } - void visit(AstEnumItem* nodep) override { + void visit(AstEnumItem* nodep) override { // FindVisitor:: // EnumItem: Remember its name for later resolution iterateChildren(nodep); // Find under either a task or the module's vars @@ -1601,7 +1601,7 @@ class LinkDotFindVisitor final : public VNVisitor { } if (ins) m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); } - void visit(AstPackageImport* nodep) override { + void visit(AstPackageImport* nodep) override { // FindVisitor:: UINFO(4, " Link: " << nodep << endl); if (!nodep->packagep()) return; // Errored in V3LinkCells VSymEnt* const srcp = m_statep->getNodeSym(nodep->packagep()); @@ -1620,7 +1620,7 @@ class LinkDotFindVisitor final : public VNVisitor { UINFO(9, " Link Done: " << nodep << endl); // No longer needed, but can't delete until any multi-instantiated modules are expanded } - void visit(AstPackageExport* nodep) override { + void visit(AstPackageExport* nodep) override { // FindVisitor:: UINFO(9, " Link: " << nodep << endl); if (!nodep->packagep()) return; // Errored in V3LinkCells VSymEnt* const srcp = m_statep->getNodeSym(nodep->packagep()); @@ -1635,13 +1635,13 @@ class LinkDotFindVisitor final : public VNVisitor { UINFO(9, " Link Done: " << nodep << endl); // No longer needed, but can't delete until any multi-instantiated modules are expanded } - void visit(AstPackageExportStarStar* nodep) override { + void visit(AstPackageExportStarStar* nodep) override { // FindVisitor:: UINFO(4, " Link: " << nodep << endl); m_curSymp->exportStarStar(m_statep->symsp()); // No longer needed, but can't delete until any multi-instantiated modules are expanded } - void visit(AstNodeForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { // FindVisitor:: // Symbol table needs nodep->name() as the index variable's name VL_RESTORER(m_curSymp); { @@ -1692,7 +1692,7 @@ class LinkDotFindVisitor final : public VNVisitor { } } - void visit(AstWithParse* nodep) override { + void visit(AstWithParse* nodep) override { // FindVisitor:: // Change WITHPARSE(FUNCREF, equation) to FUNCREF(WITH(equation)) AstNodeFTaskRef* funcrefp = VN_CAST(nodep->funcrefp(), NodeFTaskRef); if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot)) @@ -1726,7 +1726,7 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->replaceWith(nodep->funcrefp()->unlinkFrBack()); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - void visit(AstWith* nodep) override { + void visit(AstWith* nodep) override { // FindVisitor:: // Symbol table needs nodep->name() as the index variable's name // Iteration will pickup the AstVar we made under AstWith VL_RESTORER(m_curSymp); @@ -1744,7 +1744,7 @@ class LinkDotFindVisitor final : public VNVisitor { } } - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildren(nodep); } // FindVisitor:: public: // CONSTRUCTORS @@ -1792,9 +1792,9 @@ class LinkDotParamVisitor final : public VNVisitor { } // VISITORS - void visit(AstTypeTable*) override {} - void visit(AstConstPool*) override {} - void visit(AstNodeModule* nodep) override { + void visit(AstTypeTable*) override {} // ParamVisitor:: + void visit(AstConstPool*) override {} // ParamVisitor:: + void visit(AstNodeModule* nodep) override { // ParamVisitor:: UINFO(5, " " << nodep << endl); if ((nodep->dead() || !nodep->user4()) && !nodep->hierParams()) { UINFO(4, "Mark dead module " << nodep << endl); @@ -1811,7 +1811,7 @@ class LinkDotParamVisitor final : public VNVisitor { m_modp = nodep; iterateChildren(nodep); } - void visit(AstPin* nodep) override { + void visit(AstPin* nodep) override { // ParamVisitor:: // Pin: Link to submodule's port // Deal with implicit definitions - do before Resolve visitor as may // be referenced above declaration @@ -1820,7 +1820,7 @@ class LinkDotParamVisitor final : public VNVisitor { pinImplicitExprRecurse(nodep->exprp()); } } - void visit(AstDefParam* nodep) override { + void visit(AstDefParam* nodep) override { // ParamVisitor:: iterateChildren(nodep); nodep->v3warn(DEFPARAM, "defparam is deprecated (IEEE 1800-2023 C.4.1)\n" << nodep->warnMore() @@ -1843,7 +1843,7 @@ class LinkDotParamVisitor final : public VNVisitor { } VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } - void visit(AstPort* nodep) override { + void visit(AstPort* nodep) override { // ParamVisitor:: // Port: Stash the pin number // Need to set pin numbers after varnames are created // But before we do the final resolution based on names @@ -1876,14 +1876,14 @@ class LinkDotParamVisitor final : public VNVisitor { // Ports not needed any more VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } - void visit(AstAssignW* nodep) override { + void visit(AstAssignW* nodep) override { // ParamVisitor:: // Deal with implicit definitions // We used to nodep->allowImplicit() here, but it turns out // normal "assigns" can also make implicit wires. Yuk. pinImplicitExprRecurse(nodep->lhsp()); iterateChildren(nodep); } - void visit(AstAssignAlias* nodep) override { + void visit(AstAssignAlias* nodep) override { // ParamVisitor:: // tran gates need implicit creation // As VarRefs don't exist in forPrimary, sanity check UASSERT_OBJ(!m_statep->forPrimary(), nodep, "Assign aliases unexpected pre-dot"); @@ -1895,13 +1895,13 @@ class LinkDotParamVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstImplicit* nodep) override { + void visit(AstImplicit* nodep) override { // ParamVisitor:: // Unsupported gates need implicit creation pinImplicitExprRecurse(nodep->exprsp()); // We're done with implicit gates VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } - void visit(AstClassOrPackageRef* nodep) override { + void visit(AstClassOrPackageRef* nodep) override { // ParamVisitor:: if (auto* const fwdp = VN_CAST(nodep->classOrPackageNodep(), TypedefFwd)) { // Relink forward definitions to the "real" definition VSymEnt* const foundp = m_statep->getNodeSym(fwdp)->findIdFallback(fwdp->name()); @@ -1923,14 +1923,14 @@ class LinkDotParamVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstPull* nodep) override { + void visit(AstPull* nodep) override { // ParamVisitor:: // Deal with implicit definitions // We used to nodep->allowImplicit() here, but it turns out // normal "assigns" can also make implicit wires. Yuk. pinImplicitExprRecurse(nodep->lhsp()); iterateChildren(nodep); } - void visit(AstTypedefFwd* nodep) override { + void visit(AstTypedefFwd* nodep) override { // ParamVisitor:: VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name()); if (!foundp && v3Global.opt.pedantic() && nodep->name() != "process") { // Process is dangling as isn't implemented yet @@ -1946,7 +1946,7 @@ class LinkDotParamVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildren(nodep); } // ParamVisitor:: public: // CONSTRUCTORS @@ -1968,12 +1968,12 @@ class LinkDotScopeVisitor final : public VNVisitor { VSymEnt* m_modSymp = nullptr; // Symbol entry for current module // VISITORS - void visit(AstNetlist* nodep) override { + void visit(AstNetlist* nodep) override { // ScopeVisitor:: // Recurse..., backward as must do packages before using packages iterateChildrenBackwardsConst(nodep); } - void visit(AstConstPool*) override {} - void visit(AstScope* nodep) override { + void visit(AstConstPool*) override {} // ScopeVisitor:: + void visit(AstScope* nodep) override { // ScopeVisitor:: UINFO(8, " SCOPE " << nodep << endl); UASSERT_OBJ(m_statep->forScopeCreation(), nodep, "Scopes should only exist right after V3Scope"); @@ -1985,7 +1985,7 @@ class LinkDotScopeVisitor final : public VNVisitor { m_scopep = nodep; iterateChildren(nodep); } - void visit(AstVarScope* nodep) override { + void visit(AstVarScope* nodep) override { // ScopeVisitor:: if (!nodep->varp()->isFuncLocal() && !nodep->varp()->isClassMember()) { VSymEnt* const varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, nullptr); @@ -2023,23 +2023,25 @@ class LinkDotScopeVisitor final : public VNVisitor { } } } - void visit(AstNodeFTask* nodep) override { + void visit(AstNodeFTask* nodep) override { // ScopeVisitor:: VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr); symp->fallbackp(m_modSymp); iterateChildren(nodep); } - void visit(AstNodeForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { // ScopeVisitor:: VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr); symp->fallbackp(m_modSymp); // No recursion, we don't want to pick up variables } - void visit(AstConstraintForeach* nodep) override { iterateChildren(nodep); } - void visit(AstWith* nodep) override { + void visit(AstConstraintForeach* nodep) override { // ScopeVisitor:: + iterateChildren(nodep); + } + void visit(AstWith* nodep) override { // ScopeVisitor:: VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr); symp->fallbackp(m_modSymp); // No recursion, we don't want to pick up variables } - void visit(AstAssignAlias* nodep) override { + void visit(AstAssignAlias* nodep) override { // ScopeVisitor:: // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) // we'll need to replace it with a VARXREF(aliased_to) if (debug() >= 9) nodep->dumpTree("- alias: "); @@ -2049,7 +2051,7 @@ class LinkDotScopeVisitor final : public VNVisitor { fromVscp->user2p(toVscp); iterateChildren(nodep); } - void visit(AstAssignVarScope* nodep) override { + void visit(AstAssignVarScope* nodep) override { // ScopeVisitor:: UINFO(5, "ASSIGNVARSCOPE " << nodep << endl); if (debug() >= 9) nodep->dumpTree("- avs: "); VSymEnt* rhsSymp; @@ -2109,9 +2111,9 @@ class LinkDotScopeVisitor final : public VNVisitor { } // For speed, don't recurse things that can't have scope // Note we allow AstNodeStmt's as generates may be under them - void visit(AstCell*) override {} - void visit(AstVar*) override {} - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstCell*) override {} // ScopeVisitor:: + void visit(AstVar*) override {} // ScopeVisitor:: + void visit(AstNode* nodep) override { iterateChildren(nodep); } // ScopeVisitor:: public: // CONSTRUCTORS @@ -2132,7 +2134,7 @@ class LinkDotIfaceVisitor final : public VNVisitor { VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert // VISITORS - void visit(AstModport* nodep) override { + void visit(AstModport* nodep) override { // IfaceVisitor:: // Modport: Remember its name for later resolution UINFO(5, " fiv: " << nodep << endl); VL_RESTORER(m_curSymp); @@ -2143,7 +2145,7 @@ class LinkDotIfaceVisitor final : public VNVisitor { iterateChildren(nodep); } } - void visit(AstModportFTaskRef* nodep) override { + void visit(AstModportFTaskRef* nodep) override { // IfaceVisitor:: UINFO(5, " fif: " << nodep << endl); iterateChildren(nodep); if (nodep->isExport()) nodep->v3warn(E_UNSUPPORTED, "Unsupported: modport export"); @@ -2166,7 +2168,7 @@ class LinkDotIfaceVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); } } - void visit(AstModportVarRef* nodep) override { + void visit(AstModportVarRef* nodep) override { // IfaceVisitor:: UINFO(5, " fiv: " << nodep << endl); iterateChildren(nodep); VSymEnt* const symp = m_curSymp->findIdFallback(nodep->name()); @@ -2191,7 +2193,7 @@ class LinkDotIfaceVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); } } - void visit(AstNode* nodep) override { iterateChildren(nodep); } + void visit(AstNode* nodep) override { iterateChildren(nodep); } // IfaceVisitor:: public: // CONSTRUCTORS @@ -4434,39 +4436,33 @@ public: //###################################################################### // Link class functions +void V3LinkDot::dumpSubstep(const string& name) { + if (dumpTreeEitherLevel() >= 9) { + V3Global::dumpCheckGlobalTree(name); + } else if (debug() >= 5) { // on high dbg level, dump even if not explicitly told to + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(name + ".tree")); + } +} + void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { VIsCached::clearCacheTree(); // Avoid using any stale isPure - if (dumpTreeEitherLevel() >= 9) { - V3Global::dumpCheckGlobalTree("prelinkdot"); - } else if (debug() >= 5) { // on high dbg level, dump even if not explicitly told to - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); - } + dumpSubstep("prelinkdot"); LinkDotState state{rootp, step}; - const LinkDotFindVisitor visitor{rootp, &state}; - if (dumpTreeEitherLevel() >= 9) { - V3Global::dumpCheckGlobalTree("prelinkdot-find"); - } else if (debug() >= 5) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); - } + + { LinkDotFindVisitor{rootp, &state}; } + dumpSubstep("prelinkdot-find"); + if (step == LDS_PRIMARY || step == LDS_PARAMED) { // Initial link stage, resolve parameters and interfaces - const LinkDotParamVisitor visitors{rootp, &state}; - if (dumpTreeEitherLevel() >= 9) { - V3Global::dumpCheckGlobalTree("prelinkdot-param"); - } else if (debug() >= 5) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); - } + { LinkDotParamVisitor{rootp, &state}; } + dumpSubstep("prelinkdot-param"); } else if (step == LDS_ARRAYED) { } else if (step == LDS_SCOPED) { // Well after the initial link when we're ready to operate on the flat design, // process AstScope's. This needs to be separate pass after whole hierarchy graph created. - const LinkDotScopeVisitor visitors{rootp, &state}; + { LinkDotScopeVisitor{rootp, &state}; } v3Global.assertScoped(true); - if (dumpTreeEitherLevel() >= 9) { - V3Global::dumpCheckGlobalTree("prelinkdot-scoped"); - } else if (debug() >= 5) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree")); - } + dumpSubstep("prelinkdot-scoped"); } else { v3fatalSrc("Bad case"); } diff --git a/src/V3LinkDot.h b/src/V3LinkDot.h index 943baceda..6af7665ef 100644 --- a/src/V3LinkDot.h +++ b/src/V3LinkDot.h @@ -28,6 +28,7 @@ enum VLinkDotStep : uint8_t { LDS_PRIMARY, LDS_PARAMED, LDS_ARRAYED, LDS_SCOPED }; class V3LinkDot final { + static void dumpSubstep(const string& name) VL_MT_DISABLED; static void linkDotGuts(AstNetlist* rootp, VLinkDotStep step) VL_MT_DISABLED; public: From f983ce4875272d4a4997659cfb4741e80c2b62cb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 27 Apr 2025 22:11:28 -0400 Subject: [PATCH 004/211] Internals: Defer DTypeRef versus IfaceDTypeRef determination into V3LinkDot, in preparation for future parser --- src/V3LinkCells.cpp | 16 +-- src/V3LinkDot.cpp | 121 +++++++++++++++++++-- src/verilog.y | 6 +- test_regress/t/t_interface_missing_bad.out | 10 +- test_regress/t/t_interface_param_genblk.v | 2 +- 5 files changed, 132 insertions(+), 23 deletions(-) diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 3542be191..f1cf0deb3 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -131,11 +131,7 @@ class LinkCellsVisitor final : public VNVisitor { AstNodeModule* findModuleSym(const string& modName) { const VSymEnt* const foundp = m_mods.rootp()->findIdFallback(modName); - if (!foundp) { - return nullptr; - } else { - return VN_AS(foundp->nodep(), NodeModule); - } + return foundp ? VN_AS(foundp->nodep(), NodeModule) : nullptr; } AstNodeModule* resolveModule(AstNode* nodep, const string& modName) { @@ -534,8 +530,14 @@ class LinkCellsVisitor final : public VNVisitor { if (pinp->name() == "") pinp->name("__paramNumber" + cvtToStr(pinp->pinNum())); } if (m_varp) { // Parser didn't know what was interface, resolve now - const AstNodeModule* const varModp = findModuleSym(nodep->name()); - if (VN_IS(varModp, Iface)) m_varp->setIfaceRef(); + AstNodeModule* const varModp = findModuleSym(nodep->name()); + if (AstIface* const ifacep = VN_CAST(varModp, Iface)) { + // Might be an interface, but might also not really be due to interface being + // hidden by another declaration. Assume it is relevant and order as-if. + // This is safe because an interface cannot instantiate a module, so false + // module->interface edges are harmless. + newEdge(vertex(m_modp), vertex(ifacep), 1, false); + } } } void visit(AstClassOrPackageRef* nodep) override { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index ba749b975..683bc3a50 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -154,6 +154,7 @@ private: // MEMBERS VSymGraph m_syms; // Symbol table by hierarchy + VSymGraph m_mods; // Symbol table of all module names VSymEnt* m_dunitEntp = nullptr; // $unit entry std::multimap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name @@ -209,10 +210,12 @@ public: // CONSTRUCTORS LinkDotState(AstNetlist* rootp, VLinkDotStep step) : m_syms{rootp} + , m_mods{rootp} , m_step(step) { UINFO(4, __FUNCTION__ << ": " << endl); s_errorThisp = this; V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self + readModNames(); } ~LinkDotState() { V3Error::errorExitCb(nullptr); @@ -262,6 +265,18 @@ public: } } + AstNodeModule* findModuleSym(const string& modName) { + const VSymEnt* const foundp = m_mods.rootp()->findIdFallback(modName); + return foundp ? VN_AS(foundp->nodep(), NodeModule) : nullptr; + } + void readModNames() { + // Look at all modules, and store pointers to all module names + for (AstNodeModule *nextp, *nodep = v3Global.rootp()->modulesp(); nodep; nodep = nextp) { + nextp = VN_AS(nodep->nextp(), NodeModule); + m_mods.rootp()->insert(nodep->name(), new VSymEnt{&m_mods, nodep}); + } + } + VSymEnt* rootEntp() const { return m_syms.rootp(); } VSymEnt* dunitEntp() const { return m_dunitEntp; } void checkDuplicate(VSymEnt* lookupSymp, AstNode* nodep, const string& name) { @@ -427,7 +442,7 @@ public: abovep->reinsert(name, symp); return symp; } - static bool existsModScope(AstNodeModule* nodep) { return nodep->user1p() != nullptr; } + static bool existsNodeSym(AstNode* nodep) { return nodep->user1p() != nullptr; } static VSymEnt* getNodeSym(AstNode* nodep) { // Don't use this in ResolveVisitor, as we need to pick up the proper // reference under each SCOPE @@ -1114,6 +1129,10 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->user1p(m_curSymp); iterateChildren(nodep); } + void visit(AstRefDType* nodep) override { // FindVisitor:: + nodep->user1p(m_curSymp); + iterateChildren(nodep); + } void visit(AstNodeBlock* nodep) override { // FindVisitor:: UINFO(5, " " << nodep << endl); if (nodep->name() == "" && nodep->unnamed()) { @@ -1494,8 +1513,7 @@ class LinkDotFindVisitor final : public VNVisitor { } } } - VSymEnt* const insp - = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); if (m_statep->forPrimary() && nodep->isGParam()) { ++m_paramNum; VSymEnt* const symp @@ -1503,13 +1521,6 @@ class LinkDotFindVisitor final : public VNVisitor { nodep, m_classOrPackagep); symp->exported(false); } - AstIfaceRefDType* const ifacerefp - = LinkDotState::ifaceRefFromArray(nodep->subDTypep()); - if (ifacerefp) { - // Can't resolve until interfaces and modport names are - // known; see notes at top - m_statep->insertIfaceVarSym(insp); - } } } } @@ -1758,9 +1769,94 @@ public: //====================================================================== +class LinkDotFindIfaceVisitor final : public VNVisitor { + // NODE STATE + // *::user1p() -> See LinkDotState + + // STATE - for current visit position (use VL_RESTORER) + LinkDotState* const m_statep; // State to pass between visitors, including symbol table + AstNode* m_declp = nullptr; // Current declaring object that may soon contain IfaceRefDType + + // METHODS + const VSymEnt* findNonDeclSym(VSymEnt* symp, const string& name) { + // Find if there is a symbol of given name, ignoring the node that is declaring + // it (m_declp) itself. Thus if searching for "ifc" (an interface): + // + // module x; // Finishes here and finds the typedef + // typedef foo ifc; + // ifc ifc; // symp starts pointing here, but matches m_declp + while (symp) { + const VSymEnt* const foundp = symp->findIdFlat(name); + if (foundp && foundp->nodep() != m_declp) return foundp; + symp = symp->fallbackp(); + } + return nullptr; + } + + // VISITORS + void visit(AstRefDType* nodep) override { // FindIfaceVisitor:: + if (m_statep->forPrimary() && !nodep->classOrPackagep()) { + UINFO(9, " FindIfc: " << nodep << endl); + // If under a var, ignore the var itself as might be e.g. "intf intf;" + // Critical tests: + // t_interface_param_genblk.v // Checks this does make interface + // t_interface_hidden.v // Checks this doesn't making interface when hidden + if (m_statep->existsNodeSym(nodep)) { + VSymEnt* symp = m_statep->getNodeSym(nodep); + const VSymEnt* foundp = findNonDeclSym(symp, nodep->name()); + AstNode* foundNodep = nullptr; + // This: v4make test_regress/t/t_interface_param_genblk.py --debug + // --debugi-V3LinkDot 9 Passes with this commented out: + if (foundp) foundNodep = foundp->nodep(); + if (!foundNodep) foundNodep = m_statep->findModuleSym(nodep->name()); + if (foundNodep) UINFO(9, " Ifc foundNodep " << foundNodep << endl); + if (AstIface* const defp = VN_CAST(foundNodep, Iface)) { + // Must be found as module name, and not hidden/ by normal symbol (foundp) + AstIfaceRefDType* const newp + = new AstIfaceRefDType{nodep->fileline(), "", nodep->name()}; + if (nodep->paramsp()) + newp->addParamsp(nodep->paramsp()->unlinkFrBackWithNext()); + newp->ifacep(defp); + newp->user1u(nodep->user1u()); + UINFO(9, " Resolved interface " << nodep << " => " << defp << endl); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } + } + iterateChildren(nodep); + } + void visit(AstVar* nodep) override { // FindVisitor:: + VL_RESTORER(m_declp); + m_declp = nodep; + iterateChildren(nodep); + AstIfaceRefDType* const ifacerefp = LinkDotState::ifaceRefFromArray(nodep->subDTypep()); + if (ifacerefp && m_statep->existsNodeSym(nodep)) { + // Can't resolve until interfaces and modport names are + // known; see notes at top + UINFO(9, " FindIfc Var IfaceRef " << ifacerefp << endl); + if (!ifacerefp->isVirtual()) nodep->setIfaceRef(); + VSymEnt* const symp = m_statep->getNodeSym(nodep); + m_statep->insertIfaceVarSym(symp); + } + } + void visit(AstNode* nodep) override { iterateChildren(nodep); } // FindIfaceVisitor:: + +public: + // CONSTRUCTORS + LinkDotFindIfaceVisitor(AstNetlist* rootp, LinkDotState* statep) + : m_statep{statep} { + UINFO(4, __FUNCTION__ << ": " << endl); + iterate(rootp); + } + ~LinkDotFindIfaceVisitor() override = default; +}; + +//====================================================================== + class LinkDotParamVisitor final : public VNVisitor { // NODE STATE - // Cleared on global // *::user1p() -> See LinkDotState // *::user2p() -> See LinkDotState // *::user4() -> See LinkDotState @@ -4452,6 +4548,9 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { { LinkDotFindVisitor{rootp, &state}; } dumpSubstep("prelinkdot-find"); + { LinkDotFindIfaceVisitor{rootp, &state}; } + dumpSubstep("prelinkdot-findiface"); + if (step == LDS_PRIMARY || step == LDS_PARAMED) { // Initial link stage, resolve parameters and interfaces { LinkDotParamVisitor{rootp, &state}; } diff --git a/src/verilog.y b/src/verilog.y index caed8eeb2..455f90916 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1554,8 +1554,10 @@ port: // ==IEEE: port portDirNetE id/*interface*/ portSig variable_dimensionListE sigAttrListE { // VAR for now, but V3LinkCells may call setIfcaeRef on it later $$ = $3; VARDECL(VAR); VARIO(NONE); - AstNodeDType* const dtp = new AstIfaceRefDType{$2, "", *$2}; - VARDTYPE(dtp); VARIOANSI(); + // Although know it's an interface, use AstRefDType for forward compatibility + // with future parser. V3LinkCells will convert to AstIfaceRefDType. + AstNodeDType* const dtp = new AstRefDType{$2, *$2}; + VARDTYPE(dtp); addNextNull($$, VARDONEP($$, $4, $5)); } | portDirNetE id/*interface*/ '.' idAny/*modport*/ portSig variable_dimensionListE sigAttrListE { // VAR for now, but V3LinkCells may call setIfcaeRef on it later diff --git a/test_regress/t/t_interface_missing_bad.out b/test_regress/t/t_interface_missing_bad.out index 0bfc8cedc..01956743a 100644 --- a/test_regress/t/t_interface_missing_bad.out +++ b/test_regress/t/t_interface_missing_bad.out @@ -1,11 +1,17 @@ -%Error: t/t_interface_missing_bad.v:14:4: Cannot find file containing interface: 'foo_intf' +%Error: t/t_interface_missing_bad.v:14:13: Pin is not an in/out/inout/interface: 'foo' + 14 | foo_intf foo + | ^~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_interface_missing_bad.v:14:4: Can't find typedef/interface: 'foo_intf' 14 | foo_intf foo | ^~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_interface_missing_bad.v:20:4: Cannot find file containing interface: 'foo_intf' 20 | foo_intf the_foo (); | ^~~~~~~~ %Error: t/t_interface_missing_bad.v:25:15: Found definition of 'the_foo' as a CELL but expected a variable 25 | .foo (the_foo) | ^~~~~~~ +%Error: t/t_interface_missing_bad.v:25:10: Instance attempts to connect to 'foo', but it is a variable + 25 | .foo (the_foo) + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_interface_param_genblk.v b/test_regress/t/t_interface_param_genblk.v index 67092468a..2a84ad5bb 100644 --- a/test_regress/t/t_interface_param_genblk.v +++ b/test_regress/t/t_interface_param_genblk.v @@ -25,7 +25,7 @@ module t; endmodule module sub ( - intf intf + intf intf // Having this named same "intf intf" important for V3LinkDot coverage ); if (intf.A == 10) begin From 50839725367963cedbe62888735a765c26d70568 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Apr 2025 19:34:40 -0400 Subject: [PATCH 005/211] Internals: Defer AstCast into V3LinkDot, in preparation for future parser --- src/V3Width.cpp | 6 ++++++ src/verilog.y | 3 +-- test_regress/t/t_cast_stream.py | 16 ++++++++++++++++ test_regress/t/t_cast_stream.v | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_cast_stream.py create mode 100644 test_regress/t/t_cast_stream.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 521f60169..af85e2d28 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2082,6 +2082,12 @@ class WidthVisitor final : public VNVisitor { nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); userIterate(newp, m_vup); + } else if (AstNodeDType* const refp = VN_CAST(nodep->dtp(), NodeDType)) { + refp->unlinkFrBack(); + AstNode* const newp = new AstCast{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), + VFlagChildDType{}, refp}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Cast to " << nodep->dtp()->prettyTypeName()); diff --git a/src/verilog.y b/src/verilog.y index 455f90916..696659c06 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -5150,8 +5150,7 @@ expr: // IEEE: part of expression/constant_expression/ // // expanded from simple_type ps_type_identifier (part of simple_type) // // expanded from simple_type ps_parameter_identifier (part of simple_type) | packageClassScopeE idType yP_TICK '(' expr ')' - { $$ = new AstCast{$3, $5, VFlagChildDType{}, - new AstRefDType{$2, *$2, $1, nullptr}}; } + { $$ = new AstCastParse{$3, $5, new AstRefDType{$2, *$2, $1, nullptr}}; } // | yTYPE__ETC '(' exprOrDataType ')' yP_TICK '(' expr ')' { $$ = new AstCast{$1, $7, VFlagChildDType{}, diff --git a/test_regress/t/t_cast_stream.py b/test_regress/t/t_cast_stream.py new file mode 100755 index 000000000..147fe6faf --- /dev/null +++ b/test_regress/t/t_cast_stream.py @@ -0,0 +1,16 @@ +#!/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.passes() diff --git a/test_regress/t/t_cast_stream.v b/test_regress/t/t_cast_stream.v new file mode 100644 index 000000000..8ed483e0f --- /dev/null +++ b/test_regress/t/t_cast_stream.v @@ -0,0 +1,33 @@ +// 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 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +typedef enum { + UVM_TLM_READ_COMMAND, + UVM_TLM_WRITE_COMMAND, + UVM_TLM_IGNORE_COMMAND +} uvm_tlm_command_e; + +module t(/*AUTOARG*/); + + initial begin + bit array[] = new [8]; + int unsigned m_length; + uvm_tlm_command_e m_command; + + m_length = 2; + array = '{0, 0, 0, 0, 0, 0, 1, 0}; + array = new [$bits(m_length)] (array); + m_command = uvm_tlm_command_e'({ << bit { array }}); + + `checkh(m_command, 'h40) + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 3658e5f0f135ddabbb7aecf73cd5f274cc8bfc5d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Apr 2025 21:54:58 -0400 Subject: [PATCH 006/211] Internals: Rename widthToFit, no functional change. --- src/V3AstNodeExpr.h | 6 +++--- src/V3Const.cpp | 18 +++++++++--------- src/V3EmitCFunc.cpp | 2 +- src/V3Number.cpp | 4 ++-- src/V3Number.h | 2 +- src/V3Width.cpp | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index f4109cb73..c5ac75048 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -953,7 +953,7 @@ class AstConst final : public AstNodeExpr { } else if (m_num.isString()) { dtypeSetString(); } else { - dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthMin()), + dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()), VSigning::fromBool(m_num.isSigned())); } m_num.nodep(this); @@ -1000,14 +1000,14 @@ public: : ASTGEN_SUPER_Const(fl) , m_num(this, 32, num) { m_num.width(32, false); - dtypeSetLogicUnsized(32, m_num.widthMin(), VSigning::UNSIGNED); + dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED); } class Signed32 {}; // for creator type-overload selection AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value : ASTGEN_SUPER_Const(fl) , m_num(this, 32, num) { m_num.width(32, true); - dtypeSetLogicUnsized(32, m_num.widthMin(), VSigning::SIGNED); + dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::SIGNED); } class Unsized64 {}; // for creator type-overload selection AstConst(FileLine* fl, Unsized64, uint64_t num) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 9bc4c739e..1fef6d4c0 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -317,7 +317,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { if (needsMasking) { // Reduce the masked term to the minimum known width, // to use the smallest RedXor formula - const int widthMin = maskNum.widthMin(); + const int widthMin = maskNum.widthToFit(); resultp->dtypeChgWidth(widthMin, widthMin); } resultp = new AstRedXor{fl, resultp}; @@ -1098,7 +1098,7 @@ class ConstVisitor final : public VNVisitor { // Compute how many significant bits are in the mask const AstConst* const constp = VN_AS(nodep->lhsp(), Const); - const uint32_t significantBits = constp->num().widthMin(); + const uint32_t significantBits = constp->num().widthToFit(); AstOr* const orp = VN_AS(nodep->rhsp(), Or); @@ -3551,13 +3551,13 @@ class ConstVisitor final : public VNVisitor { TREEOPA("AstNodeCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)"); TREEOP ("AstNodeCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)"); // This visit function here must allow for short-circuiting. - TREEOPS("AstCond {$condp.isZero}", "replaceWIteratedThs(nodep)"); - TREEOPS("AstCond {$condp.isNeqZero}", "replaceWIteratedRhs(nodep)"); - TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}"); - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b - TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b + TREEOPS("AstCond{$condp.isZero}", "replaceWIteratedThs(nodep)"); + TREEOPS("AstCond{$condp.isNeqZero}", "replaceWIteratedRhs(nodep)"); + TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}"); + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b + TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); // Prefer constants on left, since that often needs a shift, it lets // constant red remove the shift diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 02ee7d1d5..132fd408b 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -495,7 +495,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string } else if (nodep->num().isString()) { emitConstantString(nodep); } else if (nodep->isWide()) { - int upWidth = nodep->num().widthMin(); + int upWidth = nodep->num().widthToFit(); int chunks = 0; if (upWidth > EMITC_NUM_CONSTW * VL_EDATASIZE) { // Output e.g. 8 words in groups of e.g. 8 diff --git a/src/V3Number.cpp b/src/V3Number.cpp index cb5fc9bd4..a324e0644 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -372,7 +372,7 @@ void V3Number::create(const char* sourcep) { // If was unsized, trim width per IEEE 1800-2023 5.7.1 if (!userSized && !m_data.m_autoExtend) { - width(std::max(32, base_align * ((widthMin() + base_align - 1) / base_align)), false); + width(std::max(32, base_align * ((widthToFit() + base_align - 1) / base_align)), false); } // Z or X extend specific width values. Spec says we don't 1 extend. @@ -1131,7 +1131,7 @@ int V3Number::countZ(int lsb, int nbits) const VL_MT_SAFE { return count; } -int V3Number::widthMin() const { +int V3Number::widthToFit() const { for (int bit = width() - 1; bit > 0; bit--) { if (!bitIs0(bit)) return bit + 1; } diff --git a/src/V3Number.h b/src/V3Number.h index a18c59f81..806af9ab3 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -584,7 +584,7 @@ public: string displayed(AstNode* nodep, const string& vformat) const VL_MT_STABLE; static bool displayedFmtLegal(char format, bool isScan); // Is this a valid format letter? int width() const VL_MT_SAFE { return m_data.width(); } - int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1) + int widthToFit() const; // Minimum width that can represent this number (~== log2(num)+1) bool sized() const VL_MT_SAFE { return m_data.m_sized; } bool autoExtend() const VL_MT_SAFE { return m_data.m_autoExtend; } bool isFromString() const { return m_data.m_fromString; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index af85e2d28..cea0cb007 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1325,7 +1325,7 @@ class WidthVisitor final : public VNVisitor { } else if (nodep->num().sized()) { nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width()); } else { - nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin()); + nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthToFit()); } } // We don't size the constant until we commit the widths, as need parameters From 8da539ed8a261a34b744018d8eda6055f91a3e68 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 28 Apr 2025 22:22:50 -0400 Subject: [PATCH 007/211] Fix sign extension of signed compared with unsigned case items (#5968). --- Changes | 2 ++ src/V3Width.cpp | 2 +- test_regress/t/t_math_signed3.v | 38 +++++++++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Changes b/Changes index 69deeb0ef..22cb7979c 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,8 @@ Verilator 5.037 devel **Other:** +* Fix sign extension of signed compared with unsigned case items (#5968). + Verilator 5.036 2025-04-27 ========================== diff --git a/src/V3Width.cpp b/src/V3Width.cpp index cea0cb007..1f5f5ae18 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5076,7 +5076,7 @@ class WidthVisitor final : public VNVisitor { } // Apply width iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, - EXTEND_LHS); + EXTEND_EXP); for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { diff --git a/test_regress/t/t_math_signed3.v b/test_regress/t/t_math_signed3.v index 03b20e753..24970ac5e 100644 --- a/test_regress/t/t_math_signed3.v +++ b/test_regress/t/t_math_signed3.v @@ -48,7 +48,13 @@ module t (/*AUTOARG*/); wire [5:0] cond_b = 1'b0 ? 3'sb111 : 5'sb11111; initial `checkh(cond_b, 6'b111111); + bit cmp; + initial begin +`ifndef VERILATOR + #1; +`endif + // verilator lint_on WIDTH `checkh(bug729_yuu, 1'b0); `checkh(bug729_ysu, 1'b0); @@ -81,13 +87,37 @@ module t (/*AUTOARG*/); bug349_s = 4'sb1111 - 5'b00001; `checkh(bug349_s,33'he); + cmp = 3'sb111 == 4'b111; + `checkh(cmp, 1); + cmp = 3'sb111 == 4'sb111; + `checkh(cmp, 0); + cmp = 3'sb111 != 4'b111; + `checkh(cmp, 0); + cmp = 3'sb111 != 4'sb111; + `checkh(cmp, 1); + + cmp = 3'sb111 === 4'b111; + `checkh(cmp, 1); + cmp = 3'sb111 === 4'sb111; + `checkh(cmp, 0); + case (2'sb11) - 4'b1111: ; + 4'b1111: $stop; + default: ; + endcase + + case (sb11) + 4'b1111: $stop; + default: ; + endcase + + case (2'sb11) + 4'sb1111: ; default: $stop; endcase case (sb11) - 4'b1111: ; + 4'sb1111: ; default: $stop; endcase @@ -96,7 +126,7 @@ module t (/*AUTOARG*/); end endmodule -module sub (input [3:0] a, - output [3:0] z); +module sub(input [3:0] a, + output [3:0] z); assign z = a; endmodule From 9b3fccdcb79e27c9fbabec79584d629212e6dbef Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 29 Apr 2025 18:18:54 -0400 Subject: [PATCH 008/211] Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). --- Changes | 1 + docs/guide/warnings.rst | 10 ++++++++++ src/V3Error.h | 11 ++++++----- src/V3ParseImp.cpp | 2 +- test_regress/t/t_flag_future_bad.out | 5 +++-- test_regress/t/t_pp_underline_bad.out | 3 ++- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Changes b/Changes index 22cb7979c..acda7d5ef 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 5.037 devel **Other:** +* Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] * Fix sign extension of signed compared with unsigned case items (#5968). diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 119957f0c..bf015760f 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -167,6 +167,16 @@ List Of Warnings 'pragma protect'. Third-party pragmas not defined by IEEE 1800-2023 are ignored. + This error may be disabled with a lint_off BADSTDPRAGMA metacomment. + + +.. option:: BADVLTPRAGMA + + An error that a `/*verilator ...*/` metacomment pragma is badly formed + or not understood. + + This error may be disabled with a lint_off BADVLTPRAGMA metacomment. + .. option:: BLKANDNBLK diff --git a/src/V3Error.h b/src/V3Error.h index 988fbb775..e051147e8 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -72,6 +72,7 @@ public: ASSIGNDLY, // Assignment delays ASSIGNIN, // Assigning to input BADSTDPRAGMA, // Any error related to pragmas + BADVLTPRAGMA, // Unknown Verilator pragma BLKANDNBLK, // Blocked and non-blocking assignments to same variable BLKLOOPINIT, // Delayed assignment to array inside for loops BLKSEQ, // Blocking assignments in sequential block @@ -193,7 +194,7 @@ public: "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED", // Warnings " EC_FIRST_WARN", - "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", + "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", @@ -232,10 +233,10 @@ public: // Warnings we'll present to the user as errors // Later -Werror- options may make more of these. bool pretendError() const VL_MT_SAFE { - return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BLKANDNBLK || m_e == BLKLOOPINIT - || m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == ENDLABEL || m_e == ENUMVALUE - || m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE - || m_e == ZEROREPL // Says IEEE + return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BADVLTPRAGMA || m_e == BLKANDNBLK + || m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED + || m_e == ENDLABEL || m_e == ENUMVALUE || m_e == IMPURE || m_e == PINNOTFOUND + || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE ); } // Warnings to mention manual diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index b2126e631..d1686aaf9 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -196,7 +196,7 @@ void V3ParseImp::lexVerilatorCmtBad(FileLine* fl, const char* textp) { string cmtname; for (int i = 0; std::isalnum(cmtparse[i]); i++) cmtname += cmtparse[i]; if (!v3Global.opt.isFuture(cmtname)) { - fl->v3error("Unknown verilator comment: '" << textp << "'"); + fl->v3warn(BADVLTPRAGMA, "Unknown verilator comment: '" << textp << "'"); } } diff --git a/test_regress/t/t_flag_future_bad.out b/test_regress/t/t_flag_future_bad.out index 67a863bd5..d89fcbc03 100644 --- a/test_regress/t/t_flag_future_bad.out +++ b/test_regress/t/t_flag_future_bad.out @@ -2,10 +2,11 @@ 8 | /*verilator lint_off FUTURE1*/ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_flag_future.v:11:7: Unknown verilator comment: '/*verilator FUTURE2*/' +%Error-BADVLTPRAGMA: t/t_flag_future.v:11:7: Unknown verilator comment: '/*verilator FUTURE2*/' 11 | /*verilator FUTURE2*/ | ^~~~~~~~~~~~~~~~~~~~~ -%Error: t/t_flag_future.v:12:7: Unknown verilator comment: '/*verilator FUTURE2 blah blah*/' + ... For error description see https://verilator.org/warn/BADVLTPRAGMA?v=latest +%Error-BADVLTPRAGMA: t/t_flag_future.v:12:7: Unknown verilator comment: '/*verilator FUTURE2 blah blah*/' 12 | /*verilator FUTURE2 blah blah*/ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_pp_underline_bad.out b/test_regress/t/t_pp_underline_bad.out index 257122558..c6cf038e0 100644 --- a/test_regress/t/t_pp_underline_bad.out +++ b/test_regress/t/t_pp_underline_bad.out @@ -5,7 +5,8 @@ %Error: t/t_pp_underline_bad.v:10:19: Extra underscore in meta-comment; use /*synopsys {...}*/ not /*synopsys_{...}*/ 10 | case (1'b1) // synopsys_full_case | ^~~~~~~~~~~~~~~~~~~~~ -%Error: t/t_pp_underline_bad.v:8:4: Unknown verilator comment: '/*verilator _no_inline_module*/' +%Error-BADVLTPRAGMA: t/t_pp_underline_bad.v:8:4: Unknown verilator comment: '/*verilator _no_inline_module*/' 8 | /*verilator _no_inline_module*/ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ... For error description see https://verilator.org/warn/BADVLTPRAGMA?v=latest %Error: Exiting due to From 7d4d618d98f7e7acfe44cdffe35133b1c98205b7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 29 Apr 2025 19:23:08 -0400 Subject: [PATCH 009/211] Test: display fix --- test_regress/t/t_udp_sequential.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_regress/t/t_udp_sequential.v b/test_regress/t/t_udp_sequential.v index f8758e8e0..a3cfa33ac 100644 --- a/test_regress/t/t_udp_sequential.v +++ b/test_regress/t/t_udp_sequential.v @@ -48,7 +48,7 @@ module t (/*AUTOARG*/ if (q != 1) $stop; end else if (cycle==5) begin - $display("d = %d clk = %d cycle = %d", d, clk, cycle); + $display("d=%d clk=%d cycle=%0d", d, clk, cycle); if (q != 1) $stop; end else if (cycle==6) begin From d3016b62f5b635b7f1875430d8f6d89d9d24fe94 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 29 Apr 2025 19:23:35 -0400 Subject: [PATCH 010/211] Internals: Constructor cleanup. No functional change. --- src/V3AstNodeExpr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index c5ac75048..111edca1e 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -987,7 +987,7 @@ public: class VerilogStringLiteral {}; // for creator type-overload selection AstConst(FileLine* fl, VerilogStringLiteral, const string& str) : ASTGEN_SUPER_Const(fl) - , m_num(V3Number::VerilogStringLiteral{}, this, str) { + , m_num{V3Number::VerilogStringLiteral{}, this, str} { initWithNumber(); } AstConst(FileLine* fl, uint32_t num) @@ -1033,7 +1033,7 @@ public: class String {}; // for creator type-overload selection AstConst(FileLine* fl, String, const string& num) : ASTGEN_SUPER_Const(fl) - , m_num(V3Number::String{}, this, num) { + , m_num{V3Number::String{}, this, num} { dtypeSetString(); } class BitFalse {}; @@ -1066,14 +1066,14 @@ public: class Null {}; AstConst(FileLine* fl, Null) : ASTGEN_SUPER_Const(fl) - , m_num(V3Number::Null{}, this) { + , m_num{V3Number::Null{}, this} { dtypeSetBit(); // Events 1 bit, objects 64 bits, so autoExtend=1 and use bit here initWithNumber(); } class OneStep {}; AstConst(FileLine* fl, OneStep) : ASTGEN_SUPER_Const(fl) - , m_num(V3Number::OneStep{}, this) { + , m_num{V3Number::OneStep{}, this} { dtypeSetLogicSized(64, VSigning::UNSIGNED); initWithNumber(); } From 5ca62de1673935e367d476ab8eea39eb3606bdd3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 29 Apr 2025 19:27:38 -0400 Subject: [PATCH 011/211] Fix filename backslash escapes in C code (#5947). --- Changes | 1 + src/V3EmitXml.cpp | 4 ++-- src/V3FileLine.h | 2 ++ src/V3PreLex.l | 2 +- src/V3String.cpp | 2 +- src/V3String.h | 6 +++--- src/V3Task.cpp | 4 ++-- src/V3Timing.cpp | 2 +- src/V3Waiver.cpp | 4 ++-- test_regress/t/t_pp_line.out | 20 ++++++++++++-------- test_regress/t/t_pp_line.v | 10 ++++++---- test_regress/t/t_stop_winos_bad.out | 4 ++++ test_regress/t/t_stop_winos_bad.py | 18 ++++++++++++++++++ test_regress/t/t_stop_winos_bad.v | 17 +++++++++++++++++ 14 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 test_regress/t/t_stop_winos_bad.out create mode 100755 test_regress/t/t_stop_winos_bad.py create mode 100644 test_regress/t/t_stop_winos_bad.v diff --git a/Changes b/Changes index acda7d5ef..eba22c063 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.037 devel **Other:** * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] +* Fix filename backslash escapes in C code (#5947). * Fix sign extension of signed compared with unsigned case items (#5968). diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 52cf00368..332ca6f06 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -355,8 +355,8 @@ public: // Xml output m_os << "\n"; for (const FileLine* ifp : m_nodeModules) { - m_os << "filenameLetters() << "\" filename=\"" << ifp->filename() - << "\" language=\"" << ifp->language().ascii() << "\"/>\n"; + m_os << "filenameLetters() << "\" filename=\"" + << ifp->filenameEsc() << "\" language=\"" << ifp->language().ascii() << "\"/>\n"; } m_os << "\n"; } diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 13bbb8472..fe3a6d986 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -268,6 +268,8 @@ public: string asciiLineCol() const; int filenameno() const VL_MT_SAFE { return m_filenameno; } string filename() const VL_MT_SAFE { return singleton().numberToName(filenameno()); } + // Filename with C string escapes + string filenameEsc() const VL_MT_SAFE { return VString::quoteBackslash(filename()); } bool filenameIsGlobal() const VL_MT_SAFE { return (filename() == commandLineFilename() || filename() == builtInFilename()); } diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 7f71af112..eaa899ea3 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -237,7 +237,7 @@ bom [\357\273\277] return VP_TEXT; } "`__FILE__" { FL_FWDC; static string rtnfile; - rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filename(); + rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filenameEsc(); rtnfile += '"'; yytext = (char*)rtnfile.c_str(); yyleng = rtnfile.length(); return VP_STRING; } "`__LINE__" { FL_FWDC; diff --git a/src/V3String.cpp b/src/V3String.cpp index fc8c35e2c..05405037b 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -91,7 +91,7 @@ string VString::upcase(const string& str) VL_PURE { return result; } -string VString::quoteAny(const string& str, char tgt, char esc) { +string VString::quoteAny(const string& str, char tgt, char esc) VL_PURE { string result; for (const char c : str) { if (c == tgt) result += esc; diff --git a/src/V3String.h b/src/V3String.h index 8e1e97b3e..1c70ec96e 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -94,11 +94,11 @@ public: // Convert string to upper case (toupper) static string upcase(const string& str) VL_PURE; // Insert esc just before tgt - static string quoteAny(const string& str, char tgt, char esc); + static string quoteAny(const string& str, char tgt, char esc) VL_PURE; // Replace any \'s with \\ (two consecutive backslashes) - static string quoteBackslash(const string& str) { return quoteAny(str, '\\', '\\'); } + static string quoteBackslash(const string& str) VL_PURE { return quoteAny(str, '\\', '\\'); } // Replace any %'s with %% - static string quotePercent(const string& str) { return quoteAny(str, '%', '%'); } + static string quotePercent(const string& str) VL_PURE { return quoteAny(str, '%', '%'); } // Replace any %%'s with % static string dequotePercent(const string& str); // Surround a raw string by double quote and escape if necessary diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 14cf382bc..1cfe49025 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -685,8 +685,8 @@ class TaskVisitor final : public VNVisitor { UASSERT_OBJ(snp, refp, "Missing scoping context"); ccallp->addArgsp(snp); // __Vfilenamep - ccallp->addArgsp(new AstCExpr{refp->fileline(), - "\"" + refp->fileline()->filename() + "\"", 64, true}); + ccallp->addArgsp(new AstCExpr{ + refp->fileline(), "\"" + refp->fileline()->filenameEsc() + "\"", 64, true}); // __Vlineno ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno())); } diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 09092095b..0d6be45b7 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -654,7 +654,7 @@ class TimingControlVisitor final : public VNVisitor { void addDebugInfo(AstCMethodHard* const methodp) const { if (v3Global.opt.protectIds()) return; FileLine* const flp = methodp->fileline(); - AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filename() + '"', 0}; + AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filenameEsc() + '"', 0}; ap->dtypeSetString(); methodp->addPinsp(ap); AstCExpr* const bp = new AstCExpr{flp, cvtToStr(flp->lineno()), 0}; diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp index 8578133ad..41ef8c44b 100644 --- a/src/V3Waiver.cpp +++ b/src/V3Waiver.cpp @@ -70,8 +70,8 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, cons } std::stringstream entry; - entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" << filename << "\" -match \"" - << trimmsg << "\""; + entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" + << VString::quoteBackslash(filename) << "\" -match \"" << trimmsg << "\""; s_waiverList.push_back(entry.str()); } diff --git a/test_regress/t/t_pp_line.out b/test_regress/t/t_pp_line.out index a3e32422f..5dd46780a 100644 --- a/test_regress/t/t_pp_line.out +++ b/test_regress/t/t_pp_line.out @@ -1,16 +1,20 @@ --Info: some file:100:1: aaaaaaaa +-Info: some file:100:1: aaaaaaaa file='some file' : ... note: In instance 't' - 100 | $info("aaaaaaaa"); + 100 | $info("aaaaaaaa file='%s'", "some file"); | ^~~~~ --Info: some file:101:1: bbbbbbbb +-Info: some file:101:1: bbbbbbbb file='some file' : ... note: In instance 't' - 101 | $info("bbbbbbbb"); + 101 | $info("bbbbbbbb file='%s'", "some file"); | ^~~~~ --Info: somefile.v:200:1: cccccccc +-Info: somefile.v:200:1: cccccccc file='somefile.v' : ... note: In instance 't' - 200 | $info("cccccccc"); + 200 | $info("cccccccc file='%s'", "somefile.v"); | ^~~~~ --Info: /a/somefile.v:300:1: dddddddd +-Info: /a/somefile.v:300:1: dddddddd file='/a/somefile.v' : ... note: In instance 't' - 300 | $info("dddddddd"); + 300 | $info("dddddddd file='%s'", "/a/somefile.v"); + | ^~~~~ +-Info: C:\a\somefile.v:400:1: eeeeeeee file='C:\a\somefile.v' + : ... note: In instance 't' + 400 | $info("eeeeeeee file='%s'", "C:\\a\\somefile.v"); | ^~~~~ diff --git a/test_regress/t/t_pp_line.v b/test_regress/t/t_pp_line.v index ac991e550..efc22d470 100644 --- a/test_regress/t/t_pp_line.v +++ b/test_regress/t/t_pp_line.v @@ -6,10 +6,12 @@ module t; `line 100 "some file" 0 -$info("aaaaaaaa"); -$info("bbbbbbbb"); +$info("aaaaaaaa file='%s'", `__FILE__); +$info("bbbbbbbb file='%s'", `__FILE__); `line 200 "somefile.v" 0 -$info("cccccccc"); +$info("cccccccc file='%s'", `__FILE__); `line 300 "/a/somefile.v" 0 -$info("dddddddd"); +$info("dddddddd file='%s'", `__FILE__); +`line 400 "C:\\a\\somefile.v" 0 +$info("eeeeeeee file='%s'", `__FILE__); endmodule diff --git a/test_regress/t/t_stop_winos_bad.out b/test_regress/t/t_stop_winos_bad.out new file mode 100644 index 000000000..8f72dc0d5 --- /dev/null +++ b/test_regress/t/t_stop_winos_bad.out @@ -0,0 +1,4 @@ +Intentional stop +Filename 'C:\some\windows\path\t_stop_winos_bad.v' Length = 39 +%Error: C:\some\windows\path\t_stop_winos_bad.v:14: Verilog $stop +Aborting... diff --git a/test_regress/t/t_stop_winos_bad.py b/test_regress/t/t_stop_winos_bad.py new file mode 100755 index 000000000..12c7421fd --- /dev/null +++ b/test_regress/t/t_stop_winos_bad.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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=['-no-MMD']) + +test.execute(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_stop_winos_bad.v b/test_regress/t/t_stop_winos_bad.v new file mode 100644 index 000000000..1b9cddd79 --- /dev/null +++ b/test_regress/t/t_stop_winos_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`line 7 "C:\\some\\windows\\path\\t_stop_winos_bad.v" 0 + +module t; + localparam string FILENAME = `__FILE__; + initial begin + $write("Intentional stop\n"); + // Print length to make sure \\ counts as 1 character + $write("Filename '%s' Length = %0d\n", FILENAME, FILENAME.len()); + $stop; + end +endmodule From 4e667fabb723ec8227293a05d50125a59ac4c7ab Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 08:08:44 -0400 Subject: [PATCH 012/211] Internals: Add V3Number width-and-opAssign. No functional change. --- src/V3Const.cpp | 3 +-- src/V3Number.h | 5 +++++ src/V3Simulate.h | 3 +-- src/V3Width.cpp | 6 ++---- src/V3WidthCommit.h | 3 +-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 1fef6d4c0..c2ef9e0d5 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1368,8 +1368,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(smallerp); constp->unlinkFrBack(); - V3Number num{constp, subsize}; - num.opAssign(constp->num()); + V3Number num{constp, subsize, constp->num()}; nodep->lhsp(new AstConst{constp->fileline(), num}); VL_DO_DANGLING(pushDeletep(constp), constp); if (debug() >= 9) nodep->dumpTree("- BI(EXTEND)-ou: "); diff --git a/src/V3Number.h b/src/V3Number.h index 806af9ab3..3b46b2e05 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -466,6 +466,11 @@ public: V3Number(AstNode* nodep, int width) { // 0=unsized init(nodep, width, width > 0); } + // Construct with value, changing to new width + V3Number(AstNode* nodep, int width, const V3Number& value) { + init(nodep, width, width > 0); + opAssign(value); + } V3Number(AstNode* nodep, int width, uint32_t value, bool sized = true) { init(nodep, width, sized); m_data.num()[0].m_value = value; diff --git a/src/V3Simulate.h b/src/V3Simulate.h index c98bfeb53..c2a3f79ea 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -603,8 +603,7 @@ private: // but in reality it would yield '0's without V3Table, so force 'x' bits to '0', // to ensure the result is the same with and without V3Table. if (!m_params && VN_IS(nodep, Sel) && valuep->num().isAnyX()) { - V3Number num{valuep, valuep->width()}; - num.opAssign(valuep->num()); + V3Number num{valuep, valuep->width(), valuep->num()}; valuep->num().opBitsOne(num); } } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 1f5f5ae18..5e75473b2 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6764,8 +6764,7 @@ class WidthVisitor final : public VNVisitor { if (nodep->rhsp()->width() > 32) { if (shiftp && shiftp->num().mostSetBitP1() <= 32) { // If (number)<<96'h1, then make it into (number)<<32'h1 - V3Number num(shiftp, 32, 0); - num.opAssign(shiftp->num()); + V3Number num{shiftp, 32, shiftp->num()}; AstNode* const shiftrhsp = nodep->rhsp(); nodep->rhsp()->replaceWith(new AstConst{shiftrhsp->fileline(), num}); VL_DO_DANGLING(shiftrhsp->deleteTree(), shiftrhsp); @@ -6935,8 +6934,7 @@ class WidthVisitor final : public VNVisitor { const int expWidth = expDTypep->width(); if (constp && !constp->num().isNegative()) { // Save later constant propagation work, just right-size it. - V3Number num(nodep, expWidth); - num.opAssign(constp->num()); + V3Number num{nodep, expWidth, constp->num()}; num.isSigned(false); AstNodeExpr* const newp = new AstConst{nodep->fileline(), num}; constp->replaceWith(newp); diff --git a/src/V3WidthCommit.h b/src/V3WidthCommit.h index c2b124f0e..60af3c782 100644 --- a/src/V3WidthCommit.h +++ b/src/V3WidthCommit.h @@ -30,8 +30,7 @@ public: static AstConst* newIfConstCommitSize(AstConst* nodep) { if (((nodep->dtypep()->width() != nodep->num().width()) || !nodep->num().sized()) && !nodep->num().isString()) { // Need to force the number from unsized to sized - V3Number num{nodep, nodep->dtypep()->width()}; - num.opAssign(nodep->num()); + V3Number num{nodep, nodep->dtypep()->width(), nodep->num()}; num.isSigned(nodep->isSigned()); AstConst* const newp = new AstConst{nodep->fileline(), num}; newp->dtypeFrom(nodep); From 0664cf407c8acf61697da910888b6721e82f3c0f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 08:22:05 -0400 Subject: [PATCH 013/211] Fix constant propagation making upper bits Xs (#5969). --- Changes | 1 + src/V3Number.cpp | 10 ++++++++-- src/V3Number.h | 4 ++-- test_regress/t/t_select_c.py | 18 ++++++++++++++++++ test_regress/t/t_select_c.v | 27 +++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_select_c.py create mode 100644 test_regress/t/t_select_c.v diff --git a/Changes b/Changes index eba22c063..12858d5d7 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,7 @@ Verilator 5.037 devel * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] * Fix filename backslash escapes in C code (#5947). * Fix sign extension of signed compared with unsigned case items (#5968). +* Fix constant propagation making upper bits Xs (#5969). Verilator 5.036 2025-04-27 diff --git a/src/V3Number.cpp b/src/V3Number.cpp index a324e0644..3161a5b52 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -516,6 +516,12 @@ V3Number& V3Number::setValue1() { return *this; } +void V3Number::setBitX0(int bit) { + // Selection beyond bounds after V3Premit needs to have 0s + // in upper bits. Contrast to setAllBitsXRemoved which honors xAssign + setBit(bit, v3Global.constRemoveXs() ? 0 : 'x'); +} + V3Number& V3Number::setMask(int nbits, int lsb) { setZero(); for (int bit = lsb; bit < lsb + nbits; bit++) setBit(bit, 1); @@ -2324,7 +2330,7 @@ V3Number& V3Number::opSel(const V3Number& lhs, uint32_t msbval, uint32_t lsbval) if (ibit >= 0 && ibit < lhs.width() && ibit <= static_cast(msbval)) { setBit(bit, lhs.bitIs(ibit)); } else { - setBit(bit, 'x'); + setBitX0(bit); } ++ibit; } @@ -2345,7 +2351,7 @@ V3Number& V3Number::opSelInto(const V3Number& lhs, int lsbval, int width) { if (ibit >= 0 && ibit < lhs.width()) { setBit(bit, lhs.bitIs(ibit)); } else { - setBit(bit, 'x'); + setBitX0(bit); } ibit++; } diff --git a/src/V3Number.h b/src/V3Number.h index 3b46b2e05..231dd6ccd 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -356,6 +356,7 @@ public: V3Number& setLong(uint32_t value); V3Number& setLongS(int32_t value); V3Number& setDouble(double value); + void setBitX0(int bit); void setBit(int bit, char value) { // Note: must be initialized as number and pre-zeroed! if (bit >= m_data.width()) return; const uint32_t mask = (1UL << (bit & 31)); @@ -654,8 +655,7 @@ public: uint32_t countBits(const V3Number& ctrl) const; uint32_t countBits(const V3Number& ctrl1, const V3Number& ctrl2, const V3Number& ctrl3) const; uint32_t countOnes() const; - uint32_t - mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0. + uint32_t mostSetBitP1() const; // Highest bit set + 1, e.g. for 16 return 5, for 0 return 0 // Operators bool operator<(const V3Number& rhs) const { return isLtXZ(rhs); } diff --git a/test_regress/t/t_select_c.py b/test_regress/t/t_select_c.py new file mode 100755 index 000000000..8a08e98cd --- /dev/null +++ b/test_regress/t/t_select_c.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=['--binary --fno-expand']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_select_c.v b/test_regress/t/t_select_c.v new file mode 100644 index 000000000..c4ae30a64 --- /dev/null +++ b/test_regress/t/t_select_c.v @@ -0,0 +1,27 @@ +// 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; + // verilator lint_off WIDTH + // verilator lint_off IMPLICIT + wire [22:0] w274; + wire w412; + wire w413; + wire w509; + + assign w104 = ! w509; + assign w201 = w258 > 12'hab7; + assign w204 = 7'h7f <= w104; + wire [11:0] w258 = 3'h3 || w274; + assign w538 = w412 ? out21 : w201; + wire [16:0] w539 = w413 ? w538 : 17'h00570; + wire [21:5] out21 = w204; + assign out51 = w539[0]; + + initial begin + $display("%0d", out51); + end +endmodule From b10b22d09f62a66674e83b2b0232acbc439949c2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 18:30:19 -0400 Subject: [PATCH 014/211] Change unsupported 'tagged' into parse-level message --- src/verilog.l | 2 +- src/verilog.y | 15 ++++---- test_regress/t/t_tagged.out | 70 +++++++++++++++---------------------- test_regress/t/t_tagged.v | 13 +++++-- 4 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/verilog.l b/src/verilog.l index a7d60ad55..add1df161 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -581,7 +581,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "string" { FL; return ySTRING; } "struct" { FL; return ySTRUCT; } "super" { FL; return ySUPER; } - "tagged" { ERROR_RSVD_WORD("SystemVerilog 2005"); } + "tagged" { FL; return yTAGGED; } "this" { FL; return yTHIS; } "throughout" { FL; return yTHROUGHOUT; } "timeprecision" { FL; return yTIMEPRECISION; } diff --git a/src/verilog.y b/src/verilog.y index 696659c06..f88c4c94a 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -749,7 +749,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yS_UNTIL "s_until" %token yS_UNTIL_WITH "s_until_with" %token yTABLE "table" -//UNSUP %token yTAGGED "tagged" +%token yTAGGED "tagged" %token yTASK "task" %token yTHIS "this" %token yTHROUGHOUT "throughout" @@ -1080,7 +1080,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) // These prevent other conflicts %left yP_ANDANDAND %left yMATCHES -//UNSUP %left prTAGGED +%left prTAGGED //UNSUP %left prSEQ_CLOCKING // PSL op precedence @@ -2447,7 +2447,7 @@ random_qualifier: // ==IEEE: random_qualifier taggedSoftE: /*empty*/ { $$ = false; } | ySOFT { $$ = true; } - //UNSUP yTAGGED { UNSUP } + | yTAGGED { $$ = false; BBUNSUP($1, "Unsupported: tagged union"); } ; packedSigningE: @@ -4033,8 +4033,8 @@ patternNoExpr: // IEEE: pattern **Excluding Expr* { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // IEEE: "expr" excluded; expand in callers // // "yTAGGED idAny [expr]" Already part of expr - //UNSUP yTAGGED idAny/*member_identifier*/ patternNoExpr - //UNSUP { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + | yTAGGED idAny/*member_identifier*/ patternNoExpr + { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // "yP_TICKBRA patternList '}'" part of expr under assignment_pattern ; @@ -5083,8 +5083,9 @@ expr: // IEEE: part of expression/constant_expression/ | ~l~expr yINSIDE '{' range_list '}' { $$ = new AstInside{$2, $1, $4}; } // // // IEEE: tagged_union_expression - //UNSUP yTAGGED id/*member*/ %prec prTAGGED { UNSUP } - //UNSUP yTAGGED id/*member*/ %prec prTAGGED primary { UNSUP } + //UNSUP yTAGGED id/*member*/ %prec prTAGGED { $$ = $2; BBUNSUP("tagged reference"); } + // // Spec only allows primary + //UNSUP yTAGGED id/*member*/ %prec prTAGGED expr /*primary*/ { $$ = $2; BBUNSUP("tagged reference"); } // //======================// IEEE: primary/constant_primary // diff --git a/test_regress/t/t_tagged.out b/test_regress/t/t_tagged.out index 889926ef0..7cf6307f9 100644 --- a/test_regress/t/t_tagged.out +++ b/test_regress/t/t_tagged.out @@ -1,56 +1,42 @@ -%Error-UNSUPPORTED: t/t_tagged.v:9:18: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' +%Error-UNSUPPORTED: t/t_tagged.v:9:18: Unsupported: tagged union 9 | typedef union tagged { | ^~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error-UNSUPPORTED: t/t_tagged.v:10:6: Unsupported: void (for tagged unions) 10 | void m_invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged.v:18:11: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 18 | u = tagged m_invalid; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:22:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 22 | tagged m_invalid: ; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:23:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 23 | tagged m_int: $stop; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:21:16: Unsupported: matches (for tagged union) - 21 | case (u) matches +%Error: t/t_tagged.v:19:14: syntax error, unexpected tagged, expecting IDENTIFIER-for-type + 19 | u = tagged m_invalid; + | ^~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_tagged.v:24:16: Unsupported: matches (for tagged union) + 24 | case (u) matches | ^~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:26:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 26 | if (u matches tagged m_invalid) ; +%Error: t/t_tagged.v:29:9: syntax error, unexpected tagged, expecting IDENTIFIER-for-type + 29 | tagged m_invalid: ; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged.v:34:34: Unsupported: '{} tagged patterns + 34 | if (u matches tagged m_int .n) $stop; + | ^ +%Error-UNSUPPORTED: t/t_tagged.v:34:21: Unsupported: '{} tagged patterns + 34 | if (u matches tagged m_int .n) $stop; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:26:13: Unsupported: matches operator - 26 | if (u matches tagged m_invalid) ; +%Error-UNSUPPORTED: t/t_tagged.v:34:13: Unsupported: matches operator + 34 | if (u matches tagged m_int .n) $stop; | ^~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:27:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 27 | if (u matches tagged m_int .n) $stop; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:27:13: Unsupported: matches operator - 27 | if (u matches tagged m_int .n) $stop; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:29:11: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 29 | u = tagged m_int (123); +%Error: t/t_tagged.v:36:11: syntax error, unexpected tagged, expecting IDENTIFIER-for-type + 36 | u = tagged m_int (123); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:33:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 33 | tagged m_invalid: $stop; +%Error: t/t_tagged.v:40:9: syntax error, unexpected tagged, expecting IDENTIFIER-for-type + 40 | tagged m_invalid: $stop; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:34:9: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 34 | tagged m_int .n: if (n !== 123) $stop; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:32:16: Unsupported: matches (for tagged union) - 32 | case (u) matches - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:37:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 37 | if (u matches tagged m_invalid) $stop; +%Error-UNSUPPORTED: t/t_tagged.v:45:34: Unsupported: '{} tagged patterns + 45 | if (u matches tagged m_int .n) if (n != 123) $stop; + | ^ +%Error-UNSUPPORTED: t/t_tagged.v:45:21: Unsupported: '{} tagged patterns + 45 | if (u matches tagged m_int .n) if (n != 123) $stop; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:37:13: Unsupported: matches operator - 37 | if (u matches tagged m_invalid) $stop; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:38:21: Unsupported: SystemVerilog 2005 reserved word not implemented: 'tagged' - 38 | if (u matches tagged m_int .n) if (n != 123) $stop; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged.v:38:13: Unsupported: matches operator - 38 | if (u matches tagged m_int .n) if (n != 123) $stop; +%Error-UNSUPPORTED: t/t_tagged.v:45:13: Unsupported: matches operator + 45 | if (u matches tagged m_int .n) if (n != 123) $stop; | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_tagged.v b/test_regress/t/t_tagged.v index 76bd224cd..6a8f5375f 100644 --- a/test_regress/t/t_tagged.v +++ b/test_regress/t/t_tagged.v @@ -15,9 +15,16 @@ module t(/*AUTOARG*/); string s; initial begin - u = tagged m_invalid; - s = $sformatf("%p", u); - $display("%s e.g. '{tagged m_invalid:void}", s); + begin + u = tagged m_invalid; + s = $sformatf("%p", u); + $display("%s e.g. '{tagged m_invalid:void}", s); + end + + case (u) matches + default: ; + endcase + case (u) matches tagged m_invalid: ; tagged m_int: $stop; From cbf46d0ded103bc59da99be9259d4d456e8c5d7c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 19:00:17 -0400 Subject: [PATCH 015/211] Tests: Add mis-include test. --- test_regress/t/t_config_include_bad.out | 9 +++++++++ test_regress/t/t_config_include_bad.py | 18 ++++++++++++++++++ test_regress/t/t_config_include_bad.v | 10 ++++++++++ test_regress/t/t_dist_warn_coverage.py | 1 - 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test_regress/t/t_config_include_bad.out create mode 100755 test_regress/t/t_config_include_bad.py create mode 100644 test_regress/t/t_config_include_bad.v diff --git a/test_regress/t/t_config_include_bad.out b/test_regress/t/t_config_include_bad.out new file mode 100644 index 000000000..b7cc607aa --- /dev/null +++ b/test_regress/t/t_config_include_bad.out @@ -0,0 +1,9 @@ +%Error-UNSUPPORTED: t/t_config_include_bad.v:7:1: Unsupported: Verilog 2001-config reserved word not implemented; suggest you want `include instead: 'include' + 7 | include "meant_to_tick_include.v" + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_config_include_bad.v:7:9: syntax error, unexpected STRING + 7 | include "meant_to_tick_include.v" + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_config_include_bad.py b/test_regress/t/t_config_include_bad.py new file mode 100755 index 000000000..acbdae169 --- /dev/null +++ b/test_regress/t/t_config_include_bad.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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('linter') + +test.lint(verilator_flags2=["--lint-only -Wwarn-REALCVT"], + fails=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_config_include_bad.v b/test_regress/t/t_config_include_bad.v new file mode 100644 index 000000000..696d8f8e6 --- /dev/null +++ b/test_regress/t/t_config_include_bad.v @@ -0,0 +1,10 @@ +// 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 + +include "meant_to_tick_include.v" + +module t; +endmodule diff --git a/test_regress/t/t_dist_warn_coverage.py b/test_regress/t/t_dist_warn_coverage.py index 1c7f19f40..0a973ec52 100755 --- a/test_regress/t/t_dist_warn_coverage.py +++ b/test_regress/t/t_dist_warn_coverage.py @@ -110,7 +110,6 @@ for s in [ 'Unsupported: \'{} tagged patterns', 'Unsupported: always[] (in property expression)', 'Unsupported: assertion items in clocking blocks', - 'Unsupported: default clocking identifier', 'Unsupported: don\'t know how to deal with ', 'Unsupported: eventually[] (in property expression)', 'Unsupported: extern forkjoin', From 38dd9a344e5cd8fed248ae34e5932bd8d3812a35 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 20:32:30 -0400 Subject: [PATCH 016/211] Improve documentation for BADVLTPRAGMA --- docs/gen/ex_BADVLTPRAGMA_faulty.rst | 5 +++++ docs/gen/ex_BADVLTPRAGMA_msg.rst | 7 +++++++ docs/guide/warnings.rst | 8 ++++++++ test_regress/driver.py | 1 + test_regress/t/t_dist_error_format.py | 7 +++++-- test_regress/t/t_lint_badvltpragma_bad.out | 5 +++++ test_regress/t/t_lint_badvltpragma_bad.py | 24 ++++++++++++++++++++++ test_regress/t/t_lint_badvltpragma_bad.v | 9 ++++++++ 8 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 docs/gen/ex_BADVLTPRAGMA_faulty.rst create mode 100644 docs/gen/ex_BADVLTPRAGMA_msg.rst create mode 100644 test_regress/t/t_lint_badvltpragma_bad.out create mode 100755 test_regress/t/t_lint_badvltpragma_bad.py create mode 100644 test_regress/t/t_lint_badvltpragma_bad.v diff --git a/docs/gen/ex_BADVLTPRAGMA_faulty.rst b/docs/gen/ex_BADVLTPRAGMA_faulty.rst new file mode 100644 index 000000000..62725c09b --- /dev/null +++ b/docs/gen/ex_BADVLTPRAGMA_faulty.rst @@ -0,0 +1,5 @@ +.. comment: generated by t_lint_badvltpragma_bad +.. code-block:: sv + :emphasize-lines: 1 + + // verilator lintt_off WIDTH //<--- Warning (lint_off misspelled) diff --git a/docs/gen/ex_BADVLTPRAGMA_msg.rst b/docs/gen/ex_BADVLTPRAGMA_msg.rst new file mode 100644 index 000000000..ea57d297e --- /dev/null +++ b/docs/gen/ex_BADVLTPRAGMA_msg.rst @@ -0,0 +1,7 @@ +.. comment: generated by t_lint_badvltpragma_bad +.. code-block:: + :emphasize-lines: 1,2 + + %Error-BADVLTPRAGMA: example.v:1:4 Unknown verilator comment: '/*verilator lintt_off WIDTH <--- Warning (lint_off misspelled)*/' + 7 | /*verilator lintt_off WIDTH <--- Warning (lint_off misspelled)*/ + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index bf015760f..b6cf9622f 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -175,6 +175,14 @@ List Of Warnings An error that a `/*verilator ...*/` metacomment pragma is badly formed or not understood. + Faulty example: + + .. include:: ../../docs/gen/ex_BADVLTPRAGMA_faulty.rst + + Results in: + + .. include:: ../../docs/gen/ex_BADVLTPRAGMA_msg.rst + This error may be disabled with a lint_off BADVLTPRAGMA metacomment. diff --git a/test_regress/driver.py b/test_regress/driver.py index fa7fd253e..d69c66da5 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -2633,6 +2633,7 @@ class VlTest: fhw.write(" :emphasize-lines: " + emph + "\n") fhw.write("\n") for line in out: + line = re.sub(r' +$', '', line) fhw.write(line) self.files_identical(temp_fn, out_filename) diff --git a/test_regress/t/t_dist_error_format.py b/test_regress/t/t_dist_error_format.py index 473dffb57..154ae7084 100755 --- a/test_regress/t/t_dist_error_format.py +++ b/test_regress/t/t_dist_error_format.py @@ -29,6 +29,7 @@ def formats(): for line in wholefile.splitlines(): lineno += 1 line = re.sub(r'(\$display|\$write).*\".*%(Error|Warning)', '', line) + line = re.sub(r'<---.*', '', line) if (re.search(r'(Error|Warning)', line) and not re.search(r'^\s* Date: Wed, 30 Apr 2025 22:00:06 -0400 Subject: [PATCH 017/211] Add PROCINITASSIGN on initial assignments to process variables (#2481). --- Changes | 1 + docs/gen/ex_PROCASSINIT_faulty.rst | 12 ++++ docs/gen/ex_PROCASSINIT_fixed.rst | 15 +++++ docs/gen/ex_PROCASSINIT_msg.rst | 12 ++++ docs/guide/exe_verilator.rst | 6 +- docs/guide/warnings.rst | 31 +++++++++- examples/make_protect_lib/secret_impl.v | 23 +++++--- examples/make_protect_lib/top.v | 24 +++++--- src/V3Error.h | 10 ++-- src/V3Undriven.cpp | 40 ++++++++++++- test_regress/t/t_EXAMPLE.v | 2 +- test_regress/t/t_delay.v | 2 +- test_regress/t/t_format_wide_decimal.v | 6 +- test_regress/t/t_lint_procassinit_bad.out | 21 +++++++ test_regress/t/t_lint_procassinit_bad.py | 30 ++++++++++ test_regress/t/t_lint_procassinit_bad.v | 56 +++++++++++++++++++ .../t/t_lint_removed_unused_loop_bad.v | 12 ++-- test_regress/t/t_net_delay.out | 44 +++++++-------- test_regress/t/t_net_delay.v | 4 +- 19 files changed, 292 insertions(+), 59 deletions(-) create mode 100644 docs/gen/ex_PROCASSINIT_faulty.rst create mode 100644 docs/gen/ex_PROCASSINIT_fixed.rst create mode 100644 docs/gen/ex_PROCASSINIT_msg.rst create mode 100644 test_regress/t/t_lint_procassinit_bad.out create mode 100755 test_regress/t/t_lint_procassinit_bad.py create mode 100644 test_regress/t/t_lint_procassinit_bad.v diff --git a/Changes b/Changes index 12858d5d7..786656ca0 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.037 devel **Other:** * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] +* Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix constant propagation making upper bits Xs (#5969). diff --git a/docs/gen/ex_PROCASSINIT_faulty.rst b/docs/gen/ex_PROCASSINIT_faulty.rst new file mode 100644 index 000000000..c21130841 --- /dev/null +++ b/docs/gen/ex_PROCASSINIT_faulty.rst @@ -0,0 +1,12 @@ +.. comment: generated by t_lint_procassinit_bad +.. code-block:: sv + :linenos: + :emphasize-lines: 1,5 + + logic flop_out = 1; // <--- Warning + + always @(posedge clk, negedge reset_l) begin + if (enable) begin + flop_out <= ~in; // <--- Use of initialized + end + end diff --git a/docs/gen/ex_PROCASSINIT_fixed.rst b/docs/gen/ex_PROCASSINIT_fixed.rst new file mode 100644 index 000000000..f5a3f3711 --- /dev/null +++ b/docs/gen/ex_PROCASSINIT_fixed.rst @@ -0,0 +1,15 @@ +.. comment: generated by t_lint_procassinit_bad +.. code-block:: sv + :linenos: + :emphasize-lines: 5 + + logic flop2_out; + + always @(posedge clk, negedge reset_l) begin + if (!reset_l) begin + flop2_out <= '1; // <--- Added reset init + end + else if (enable) begin + flop2_out <= ~in; + end + end diff --git a/docs/gen/ex_PROCASSINIT_msg.rst b/docs/gen/ex_PROCASSINIT_msg.rst new file mode 100644 index 000000000..4fe084952 --- /dev/null +++ b/docs/gen/ex_PROCASSINIT_msg.rst @@ -0,0 +1,12 @@ +.. comment: generated by t_lint_procassinit_bad +.. code-block:: + + %Warning-PROCASSINIT: example.v:1:21 Procedural assignment to declaration with initial value: 'flop_out' + : ... note: In instance 't' + : ... Location of variable initialization + 26 | logic flop_out = 1; + | ^ + example.v:1:10 ... Location of variable process write + : ... Perhaps should initialize instead using a reset in this process + 30 | flop_out <= ~in; + | ^~~~~~~~ diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 2f2b1cdfe..c75d21f3a 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1880,9 +1880,9 @@ Summary: ``-Wwarn-ASSIGNDLY`` ``-Wwarn-BLKSEQ`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM`` ``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-IMPORTSTAR`` ``-Wwarn-INCABSPATH`` ``-Wwarn-PINCONNECTEMPTY`` - ``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN`` - ``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDLOOP`` ``-Wwarn-UNUSEDPARAM`` - ``-Wwarn-UNUSEDSIGNAL`` ``-Wwarn-VARHIDDEN``. + ``-Wwarn-PINNOCONNECT`` ``-Wwarn-PROCASSINIT`` ``-Wwarn-SYNCASYNCNET`` + ``-Wwarn-UNDRIVEN`` ``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDLOOP`` + ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL`` ``-Wwarn-VARHIDDEN``. .. option:: --x-assign diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index b6cf9622f..9e03d38d2 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1471,6 +1471,35 @@ List Of Warnings a var/reg must be used as the target of procedural assignments. +.. option:: PROCINITASSIGN + + Warns that the specified signal is given an initial value where it is + declared, and is also driven in an always process. Typically such + initial values should instead be set using a reset signal inside the + process, to match requirements of hardware synthesis tools. + + Faulty example: + + .. include:: ../../docs/gen/ex_PROCINITASSIGN_faulty.rst + + Results in: + + .. include:: ../../docs/gen/ex_PROCINITASSIGN_msg.rst + + One possible fix, adding a reset to the always: + + .. include:: ../../docs/gen/ex_PROCINITASSIGN_fixed.rst + + Alternatively, use an initial block for the initialization: + + .. code-block:: sv + + initial flop_out = 1; // <--- Fixed + + Disabled by default as this is a code-style warning; it will simulate + correctly. + + .. option:: PROFOUTOFDATE Warns that threads were scheduled using estimated costs, even though @@ -2164,7 +2193,7 @@ List Of Warnings .. include:: ../../docs/gen/ex_VARHIDDEN_msg.rst - To resolve this, rename the variable to an unique name. + To resolve this, rename the inner or outer variable to an unique name. .. option:: WAITCONST diff --git a/examples/make_protect_lib/secret_impl.v b/examples/make_protect_lib/secret_impl.v index 2a12a272c..dc9fe1c6b 100644 --- a/examples/make_protect_lib/secret_impl.v +++ b/examples/make_protect_lib/secret_impl.v @@ -12,19 +12,26 @@ module secret_impl input [31:0] a, input [31:0] b, output logic [31:0] x, - input clk); + input clk, + input reset_l); - logic [31:0] accum_q = 0; - logic [31:0] secret_value = 9; + logic [31:0] accum_q; + logic [31:0] secret_value; initial $display("[%0t] %m: initialized", $time); always @(posedge clk) begin - accum_q <= accum_q + a; - if (accum_q > 10) - x <= b; - else - x <= a + b + secret_value; + if (!reset_l) begin + accum_q <= 0; + secret_value <= 9; + end + else begin + accum_q <= accum_q + a; + if (accum_q > 10) + x <= b; + else + x <= a + b + secret_value; + end end endmodule diff --git a/examples/make_protect_lib/top.v b/examples/make_protect_lib/top.v index b9b71ef87..031764292 100644 --- a/examples/make_protect_lib/top.v +++ b/examples/make_protect_lib/top.v @@ -8,26 +8,36 @@ module top (input clk); - integer cyc = 0; - logic [31:0] a = 0; - logic [31:0] b = 0; + int cyc; + logic reset_l; + logic [31:0] a; + logic [31:0] b; logic [31:0] x; - verilated_secret secret (.a, .b, .x, .clk); + verilated_secret secret (.a, .b, .x, .clk, .reset_l); always @(posedge clk) begin $display("[%0t] cyc=%0d a=%0d b=%0d x=%0d", $time, cyc, a, b, x); cyc <= cyc + 1; if (cyc == 0) begin + reset_l <= 0; + a <= 0; + b <= 0; + end + else if (cyc == 1) begin + reset_l <= 1; a <= 5; b <= 7; - end else if (cyc == 1) begin + end + else if (cyc == 2) begin a <= 6; b <= 2; - end else if (cyc == 2) begin + end + else if (cyc == 3) begin a <= 1; b <= 9; - end else if (cyc > 3) begin + end + else if (cyc > 4) begin $display("Done"); $finish; end diff --git a/src/V3Error.h b/src/V3Error.h index e051147e8..591128e8e 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -129,6 +129,7 @@ public: PINNOTFOUND, // instance port name not found in it's module PKGNODECL, // Error: Package/class needs to be predeclared PREPROCZERO, // Preprocessor expression with zero + PROCASSINIT, // Procedural assignment versus initialization PROCASSWIRE, // Procedural assignment on wire PROFOUTOFDATE, // Profile data out of date PROTECTED, // detected `pragma protected @@ -206,7 +207,7 @@ public: "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY", - "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE", + "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", @@ -259,9 +260,10 @@ public: return (m_e == ASSIGNDLY // More than style, but for backward compatibility || m_e == BLKSEQ || m_e == DECLFILENAME || m_e == DEFPARAM || m_e == EOFNEWLINE || m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH - || m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET - || m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDLOOP - || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL || m_e == VARHIDDEN); + || m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == PROCASSINIT + || m_e == SYNCASYNCNET || m_e == UNDRIVEN || m_e == UNUSEDGENVAR + || m_e == UNUSEDLOOP || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL + || m_e == VARHIDDEN); } // Warnings that are unused only bool unusedError() const VL_MT_SAFE { diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 130b3f498..7e9903018 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -46,6 +46,8 @@ class UndrivenVarEntry final { const FileLine* m_alwCombFileLinep = nullptr; // File line of always_comb of var if driven // within always_comb, else nullptr const AstNodeVarRef* m_nodep = nullptr; // varref if driven, else nullptr + const AstNode* m_initStaticp = nullptr; // varref if in InitialStatic driven + const AstNode* m_procWritep = nullptr; // varref if written in process const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate @@ -129,6 +131,11 @@ public: m_alwCombp = alwCombp; m_alwCombFileLinep = fileLinep; } + + const AstNode* initStaticp() const { return m_initStaticp; } + void initStaticp(const AstNode* nodep) { m_initStaticp = nodep; } + const AstNode* procWritep() const { return m_procWritep; } + void procWritep(const AstNode* nodep) { m_procWritep = nodep; } void underGenerate() { m_underGen = true; } bool isUnderGen() const { return m_underGen; } bool isDrivenWhole() const { return m_wholeFlags[FLAG_DRIVEN]; } @@ -172,6 +179,18 @@ public: // Combine bits into overall state AstVar* const nodep = m_varp; + if (initStaticp() && procWritep() && !nodep->isClassMember() && !nodep->isFuncLocal()) { + initStaticp()->v3warn( + PROCASSINIT, + "Procedural assignment to declaration with initial value: " + << nodep->prettyNameQ() << '\n' + << initStaticp()->warnMore() << "... Location of variable initialization\n" + << initStaticp()->warnContextPrimary() << '\n' + << procWritep()->warnOther() << "... Location of variable process write\n" + << procWritep()->warnMore() + << "... Perhaps should initialize instead using a reset in this process\n" + << procWritep()->warnContextSecondary()); + } if (nodep->isGenVar()) { // Genvar if (!nodep->isIfaceRef() && !nodep->isUsedParam() && !unusedMatch(nodep)) { nodep->v3warn(UNUSEDGENVAR, "Genvar is not used: " << nodep->prettyNameQ()); @@ -277,10 +296,12 @@ class UndrivenVisitor final : public VNVisitorConst { std::array, 3> m_entryps; // Nodes to delete when finished bool m_inBBox = false; // In black box; mark as driven+used bool m_inContAssign = false; // In continuous assignment + bool m_inInitialStatic = false; // In InitialStatic bool m_inProcAssign = false; // In procedural assignment bool m_inFTaskRef = false; // In function or task call bool m_inInoutOrRefPin = false; // Connected to pin that is inout const AstNodeFTask* m_taskp = nullptr; // Current task + const AstAlways* m_alwaysp = nullptr; // Current always of either type const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr // METHODS @@ -384,9 +405,8 @@ class UndrivenVisitor final : public VNVisitorConst { nodep->v3warn(PROCASSWIRE, "Procedural assignment to wire, perhaps intended var" << " (IEEE 1800-2023 6.5): " << nodep->prettyNameQ()); - } - if (m_inContAssign && !nodep->varp()->varType().isContAssignable() - && !nodep->fileline()->language().systemVerilog()) { + } else if (m_inContAssign && !nodep->varp()->varType().isContAssignable() + && !nodep->fileline()->language().systemVerilog()) { nodep->v3warn(CONTASSREG, "Continuous assignment to reg, perhaps intended wire" << " (IEEE 1364-2005 6.1; Verilog only, legal in SV): " @@ -448,6 +468,13 @@ class UndrivenVisitor final : public VNVisitorConst { if (m_alwaysCombp) entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline()); } + if (nodep->access().isWriteOrRW()) { + UINFO(1, "ww is=" << m_inInitialStatic << " ipa=" << m_inProcAssign << " " << nodep + << endl); + if (m_inInitialStatic && !entryp->initStaticp()) entryp->initStaticp(nodep); + if (m_alwaysp && m_inProcAssign && !entryp->procWritep()) + entryp->procWritep(nodep); + } if (m_inBBox || nodep->access().isReadOrRW() || fdrv // Inouts have only isWrite set, as we don't have more @@ -480,9 +507,16 @@ class UndrivenVisitor final : public VNVisitorConst { m_inContAssign = true; iterateChildrenConst(nodep); } + void visit(AstInitialStatic* nodep) override { + VL_RESTORER(m_inInitialStatic); + m_inInitialStatic = true; + iterateChildrenConst(nodep); + } void visit(AstAlways* nodep) override { + VL_RESTORER(m_alwaysp); VL_RESTORER(m_alwaysCombp); AstNode::user2ClearTree(); + m_alwaysp = nodep; if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) { UINFO(9, " " << nodep << endl); m_alwaysCombp = nodep; diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index b00361778..a5d815b13 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -26,7 +26,7 @@ module t(/*AUTOARG*/ ); input clk; - integer cyc = 0; + int cyc; reg [63:0] crc; reg [63:0] sum; diff --git a/test_regress/t/t_delay.v b/test_regress/t/t_delay.v index e541a10d0..9f1cb5420 100644 --- a/test_regress/t/t_delay.v +++ b/test_regress/t/t_delay.v @@ -12,7 +12,7 @@ module t (/*AUTOARG*/ ); input clk; - integer cyc=1; + int cyc; reg [31:0] dly0; wire [31:0] dly1; diff --git a/test_regress/t/t_format_wide_decimal.v b/test_regress/t/t_format_wide_decimal.v index 2b2a04835..b45b816e4 100644 --- a/test_regress/t/t_format_wide_decimal.v +++ b/test_regress/t/t_format_wide_decimal.v @@ -12,8 +12,10 @@ module t_format_wide_decimal(/*AUTOARG*/ ); input clk; - int cycle = 0; - bit [1023:0] x = '1; + int cycle; + bit [1023:0] x; + + initial x = '1; always @(posedge clk) begin if (cycle == 0) begin diff --git a/test_regress/t/t_lint_procassinit_bad.out b/test_regress/t/t_lint_procassinit_bad.out new file mode 100644 index 000000000..f54e793a1 --- /dev/null +++ b/test_regress/t/t_lint_procassinit_bad.out @@ -0,0 +1,21 @@ +%Warning-PROCASSINIT: t/t_lint_procassinit_bad.v:26:21: Procedural assignment to declaration with initial value: 'flop_out' + : ... note: In instance 't' + : ... Location of variable initialization + 26 | logic flop_out = 1; + | ^ + t/t_lint_procassinit_bad.v:30:10: ... Location of variable process write + : ... Perhaps should initialize instead using a reset in this process + 30 | flop_out <= ~in; + | ^~~~~~~~ + ... For warning description see https://verilator.org/warn/PROCASSINIT?v=latest + ... Use "/* verilator lint_off PROCASSINIT */" and lint_on around source to disable this message. +%Warning-PROCASSINIT: t/t_lint_procassinit_bad.v:48:21: Procedural assignment to declaration with initial value: 'bad_comb' + : ... note: In instance 't' + : ... Location of variable initialization + 48 | logic bad_comb = 1; + | ^ + t/t_lint_procassinit_bad.v:51:7: ... Location of variable process write + : ... Perhaps should initialize instead using a reset in this process + 51 | bad_comb = ok2; + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_procassinit_bad.py b/test_regress/t/t_lint_procassinit_bad.py new file mode 100755 index 000000000..05ca0a399 --- /dev/null +++ b/test_regress/t/t_lint_procassinit_bad.py @@ -0,0 +1,30 @@ +#!/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('vlt') + +test.lint(verilator_flags2=['-Wall -Wno-DECLFILENAME'], + fails=True, + expect_filename=test.golden_filename) + +test.extract(in_filename=test.top_filename, + out_filename="../docs/gen/ex_PROCASSINIT_faulty.rst", + lines="26-32") + +test.extract(in_filename=test.top_filename, + out_filename="../docs/gen/ex_PROCASSINIT_fixed.rst", + lines="36-45") + +test.extract(in_filename=test.golden_filename, + out_filename="../docs/gen/ex_PROCASSINIT_msg.rst", + lines="1-9") + +test.passes() diff --git a/test_regress/t/t_lint_procassinit_bad.v b/test_regress/t/t_lint_procassinit_bad.v new file mode 100644 index 000000000..6b8e11d85 --- /dev/null +++ b/test_regress/t/t_lint_procassinit_bad.v @@ -0,0 +1,56 @@ +// 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, reset_l, in, enable + ); + input clk; + input reset_l; + input in; + input enable; + + logic ok1 = 1; + logic ok2 = 1; + logic ok3 = ok2; + + initial begin + ok1 = 1; + end + + //== Faulty example + + logic flop_out = 1; // <--- Warning + + always @(posedge clk, negedge reset_l) begin + if (enable) begin + flop_out <= ~in; // <--- Use of initialized + end + end + + //== Fixed example + + logic flop2_out; + + always @(posedge clk, negedge reset_l) begin + if (!reset_l) begin + flop2_out <= '1; // <--- Added reset init + end + else if (enable) begin + flop2_out <= ~in; + end + end + + // Combo version + logic bad_comb = 1; // but this is not fine + + always @* begin + bad_comb = ok2; + end + + wire _unused_ok = &{1'b0, flop_out, flop2_out, bad_comb, ok1, ok2, ok3}; + +endmodule diff --git a/test_regress/t/t_lint_removed_unused_loop_bad.v b/test_regress/t/t_lint_removed_unused_loop_bad.v index 7bd5b195d..5ec480ede 100644 --- a/test_regress/t/t_lint_removed_unused_loop_bad.v +++ b/test_regress/t/t_lint_removed_unused_loop_bad.v @@ -28,10 +28,10 @@ endmodule // module unused - no warning for any of statements inside module unused(input clk); - reg unused_variable_while = 0; - reg unused_variable_do_while = 0; - reg unused_variable_for = 0; - const logic always_false = 0; + bit unused_variable_while; + bit unused_variable_do_while; + bit unused_variable_for; + const bit always_false = 0; always @(posedge clk) begin while(unused_variable_while) begin @@ -259,7 +259,7 @@ module clock_init_race(input clk, input reset_l); logic m_3_reset = reset_l; assign m_2_clock = clk; assign m_3_clock = clk; - int m_3_counter = 0; + int m_3_counter; initial begin $write("*-* START TEST *-*\n"); end @@ -271,7 +271,7 @@ module clock_init_race(input clk, input reset_l); end end - reg m_2_ticked = 1'b0; + bit m_2_ticked; always @(posedge m_2_clock) if (!m_2_reset) begin m_2_ticked = 1'b1; end diff --git a/test_regress/t/t_net_delay.out b/test_regress/t/t_net_delay.out index eab2df885..ca9804540 100644 --- a/test_regress/t/t_net_delay.out +++ b/test_regress/t/t_net_delay.out @@ -1,37 +1,37 @@ -%Warning-STMTDLY: t/t_net_delay.v:14:11: Ignoring delay on this statement due to --no-timing +%Warning-STMTDLY: t/t_net_delay.v:16:11: Ignoring delay on this statement due to --no-timing : ... note: In instance 't' - 14 | always #2 clk = ~clk; + 16 | always #2 clk = ~clk; | ^ ... For warning description see https://verilator.org/warn/STMTDLY?v=latest ... Use "/* verilator lint_off STMTDLY */" and lint_on around source to disable this message. -%Warning-STMTDLY: t/t_net_delay.v:20:14: Ignoring delay on this statement due to --no-timing +%Warning-STMTDLY: t/t_net_delay.v:22:14: Ignoring delay on this statement due to --no-timing : ... note: In instance 't' - 20 | wire[3:0] #3 val1; + 22 | wire[3:0] #3 val1; | ^ -%Warning-STMTDLY: t/t_net_delay.v:21:14: Ignoring delay on this statement due to --no-timing - : ... note: In instance 't' - 21 | wire[3:0] #3 val2; - | ^ -%Warning-ASSIGNDLY: t/t_net_delay.v:22:14: Ignoring timing control on this assignment/primitive due to --no-timing - : ... note: In instance 't' - 22 | wire[3:0] #5 val3 = cyc; - | ^ - ... For warning description see https://verilator.org/warn/ASSIGNDLY?v=latest - ... Use "/* verilator lint_off ASSIGNDLY */" and lint_on around source to disable this message. %Warning-STMTDLY: t/t_net_delay.v:23:14: Ignoring delay on this statement due to --no-timing : ... note: In instance 't' - 23 | wire[3:0] #5 val4; + 23 | wire[3:0] #3 val2; | ^ %Warning-ASSIGNDLY: t/t_net_delay.v:24:14: Ignoring timing control on this assignment/primitive due to --no-timing : ... note: In instance 't' - 24 | wire[3:0] #3 val5 = x, val6 = cyc; + 24 | wire[3:0] #5 val3 = cyc; | ^ -%Warning-ASSIGNDLY: t/t_net_delay.v:27:11: Ignoring timing control on this assignment/primitive due to --no-timing - : ... note: In instance 't' - 27 | assign #3 val2 = cyc; - | ^ -%Warning-STMTDLY: t/t_net_delay.v:39:26: Ignoring delay on this statement due to --no-timing + ... For warning description see https://verilator.org/warn/ASSIGNDLY?v=latest + ... Use "/* verilator lint_off ASSIGNDLY */" and lint_on around source to disable this message. +%Warning-STMTDLY: t/t_net_delay.v:25:14: Ignoring delay on this statement due to --no-timing : ... note: In instance 't' - 39 | always @(posedge clk) #1 begin + 25 | wire[3:0] #5 val4; + | ^ +%Warning-ASSIGNDLY: t/t_net_delay.v:26:14: Ignoring timing control on this assignment/primitive due to --no-timing + : ... note: In instance 't' + 26 | wire[3:0] #3 val5 = x, val6 = cyc; + | ^ +%Warning-ASSIGNDLY: t/t_net_delay.v:29:11: Ignoring timing control on this assignment/primitive due to --no-timing + : ... note: In instance 't' + 29 | assign #3 val2 = cyc; + | ^ +%Warning-STMTDLY: t/t_net_delay.v:41:26: Ignoring delay on this statement due to --no-timing + : ... note: In instance 't' + 41 | always @(posedge clk) #1 begin | ^ %Error: Exiting due to diff --git a/test_regress/t/t_net_delay.v b/test_regress/t/t_net_delay.v index 514504a6b..9822f1827 100644 --- a/test_regress/t/t_net_delay.v +++ b/test_regress/t/t_net_delay.v @@ -9,14 +9,16 @@ module t; // verilator lint_off UNOPTFLAT + // verilator lint_off PROCASSINIT logic clk = 0; // verilator lint_on UNOPTFLAT + // verilator lint_on PROCASSINIT always #2 clk = ~clk; // verilator lint_off UNDRIVEN wire[3:0] x; // verilator lint_on UNDRIVEN - reg[3:0] cyc = 0; + bit [3:0] cyc; wire[3:0] #3 val1; wire[3:0] #3 val2; wire[3:0] #5 val3 = cyc; From 2c0372acdfce790e93d692175cc36b9cbe56f0ca Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Apr 2025 23:02:19 -0400 Subject: [PATCH 018/211] Tests: Fix relocation of extract tests --- test_regress/t/t_assert_comp_bad.py | 16 ++++++++-------- test_regress/t/t_lint_badvltpragma_bad.py | 9 +++++++-- test_regress/t/t_lint_didnotconverge_bad.py | 4 ++-- .../t/t_lint_didnotconverge_nodbg_bad.py | 2 +- test_regress/t/t_lint_multidriven_bad.py | 4 ++-- test_regress/t/t_lint_pinmissing_bad.py | 4 ++-- test_regress/t/t_lint_pkgnodecl_bad.py | 4 ++-- test_regress/t/t_lint_procassinit_bad.py | 11 ++++++++--- test_regress/t/t_lint_stmtdly_bad.py | 4 ++-- test_regress/t/t_lint_widthexpand_docs_bad.py | 6 +++--- test_regress/t/t_var_bad_hide_docs.py | 4 ++-- 11 files changed, 39 insertions(+), 29 deletions(-) diff --git a/test_regress/t/t_assert_comp_bad.py b/test_regress/t/t_assert_comp_bad.py index 12cec4f93..114fbe952 100755 --- a/test_regress/t/t_assert_comp_bad.py +++ b/test_regress/t/t_assert_comp_bad.py @@ -23,35 +23,35 @@ test.compile(verilator_flags2=['--assert'], expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_USERWARN_faulty.rst", + out_filename=root + "/docs/gen/ex_USERWARN_faulty.rst", regexp=r'\$warn.*User') test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_USERERROR_faulty.rst", + out_filename=root + "/docs/gen/ex_USERERROR_faulty.rst", regexp=r'\$error.*User') test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_USERINFO_faulty.rst", + out_filename=root + "/docs/gen/ex_USERINFO_faulty.rst", regexp=r'\$info.*User') test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_USERFATAL_faulty.rst", + out_filename=root + "/docs/gen/ex_USERFATAL_faulty.rst", regexp=r'\$fatal.*User') test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_USERWARN_msg.rst", + out_filename=root + "/docs/gen/ex_USERWARN_msg.rst", regexp=r'USERWARN:.* User') test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_USERERROR_msg.rst", + out_filename=root + "/docs/gen/ex_USERERROR_msg.rst", regexp=r'USERERROR:.* User') test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_USERINFO_msg.rst", + out_filename=root + "/docs/gen/ex_USERINFO_msg.rst", regexp=r'-Info:.* User') test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_USERFATAL_msg.rst", + out_filename=root + "/docs/gen/ex_USERFATAL_msg.rst", regexp=r'USERFATAL:.* User') test.passes() diff --git a/test_regress/t/t_lint_badvltpragma_bad.py b/test_regress/t/t_lint_badvltpragma_bad.py index 4ddd05c4a..1c907c181 100755 --- a/test_regress/t/t_lint_badvltpragma_bad.py +++ b/test_regress/t/t_lint_badvltpragma_bad.py @@ -11,14 +11,19 @@ import vltest_bootstrap test.scenarios('vlt') +root = ".." + +if not os.path.exists(root + "/.git"): + test.skip("Not in a git repository") + test.lint(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_BADVLTPRAGMA_faulty.rst", + out_filename=root + "/docs/gen/ex_BADVLTPRAGMA_faulty.rst", lines="7") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_BADVLTPRAGMA_msg.rst", + out_filename=root + "/docs/gen/ex_BADVLTPRAGMA_msg.rst", lines="1-3") test.passes() diff --git a/test_regress/t/t_lint_didnotconverge_bad.py b/test_regress/t/t_lint_didnotconverge_bad.py index 40e40f26e..62e56833c 100755 --- a/test_regress/t/t_lint_didnotconverge_bad.py +++ b/test_regress/t/t_lint_didnotconverge_bad.py @@ -21,11 +21,11 @@ test.compile(verilator_flags2=["--prof-cfuncs"]) test.execute(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_DIDNOTCONVERGE_faulty.rst", + out_filename=root + "/docs/gen/ex_DIDNOTCONVERGE_faulty.rst", lines="16-17") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_DIDNOTCONVERGE_msg.rst", + out_filename=root + "/docs/gen/ex_DIDNOTCONVERGE_msg.rst", lines="1-2") test.passes() diff --git a/test_regress/t/t_lint_didnotconverge_nodbg_bad.py b/test_regress/t/t_lint_didnotconverge_nodbg_bad.py index 1d53c8c93..4ca34912b 100755 --- a/test_regress/t/t_lint_didnotconverge_nodbg_bad.py +++ b/test_regress/t/t_lint_didnotconverge_nodbg_bad.py @@ -22,7 +22,7 @@ test.compile(make_flags=['CPPFLAGS_ADD=-UVL_DEBUG']) test.execute(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst", + out_filename=root + "/docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst", lines="1") test.passes() diff --git a/test_regress/t/t_lint_multidriven_bad.py b/test_regress/t/t_lint_multidriven_bad.py index ed3410fdd..f84ef04a2 100755 --- a/test_regress/t/t_lint_multidriven_bad.py +++ b/test_regress/t/t_lint_multidriven_bad.py @@ -19,11 +19,11 @@ if not os.path.exists(root + "/.git"): test.lint(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_MULTIDRIVEN_faulty.rst", + out_filename=root + "/docs/gen/ex_MULTIDRIVEN_faulty.rst", lines="31-36") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_MULTIDRIVEN_msg.rst", + out_filename=root + "/docs/gen/ex_MULTIDRIVEN_msg.rst", lines="10,11,14") test.passes() diff --git a/test_regress/t/t_lint_pinmissing_bad.py b/test_regress/t/t_lint_pinmissing_bad.py index 3f80106f9..78a6d6a13 100755 --- a/test_regress/t/t_lint_pinmissing_bad.py +++ b/test_regress/t/t_lint_pinmissing_bad.py @@ -19,11 +19,11 @@ if not os.path.exists(root + "/.git"): test.lint(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_PINMISSING_faulty.rst", + out_filename=root + "/docs/gen/ex_PINMISSING_faulty.rst", lines="7-12") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_PINMISSING_msg.rst", + out_filename=root + "/docs/gen/ex_PINMISSING_msg.rst", lines="1-1") test.passes() diff --git a/test_regress/t/t_lint_pkgnodecl_bad.py b/test_regress/t/t_lint_pkgnodecl_bad.py index 21d9be319..77102e664 100755 --- a/test_regress/t/t_lint_pkgnodecl_bad.py +++ b/test_regress/t/t_lint_pkgnodecl_bad.py @@ -19,11 +19,11 @@ if not os.path.exists(root + "/.git"): test.lint(fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_PKGNODECL_faulty.rst", + out_filename=root + "/docs/gen/ex_PKGNODECL_faulty.rst", lines="7-12") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_PKGNODECL_msg.rst", + out_filename=root + "/docs/gen/ex_PKGNODECL_msg.rst", lines="1") test.passes() diff --git a/test_regress/t/t_lint_procassinit_bad.py b/test_regress/t/t_lint_procassinit_bad.py index 05ca0a399..28f124234 100755 --- a/test_regress/t/t_lint_procassinit_bad.py +++ b/test_regress/t/t_lint_procassinit_bad.py @@ -11,20 +11,25 @@ import vltest_bootstrap test.scenarios('vlt') +root = ".." + +if not os.path.exists(root + "/.git"): + test.skip("Not in a git repository") + test.lint(verilator_flags2=['-Wall -Wno-DECLFILENAME'], fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_PROCASSINIT_faulty.rst", + out_filename=root + "/docs/gen/ex_PROCASSINIT_faulty.rst", lines="26-32") test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_PROCASSINIT_fixed.rst", + out_filename=root + "/docs/gen/ex_PROCASSINIT_fixed.rst", lines="36-45") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_PROCASSINIT_msg.rst", + out_filename=root + "/docs/gen/ex_PROCASSINIT_msg.rst", lines="1-9") test.passes() diff --git a/test_regress/t/t_lint_stmtdly_bad.py b/test_regress/t/t_lint_stmtdly_bad.py index cf06e63a2..953505d81 100755 --- a/test_regress/t/t_lint_stmtdly_bad.py +++ b/test_regress/t/t_lint_stmtdly_bad.py @@ -19,11 +19,11 @@ if not os.path.exists(root + "/.git"): test.lint(verilator_flags2=["--no-timing"], fails=True, expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_STMTDLY_faulty.rst", + out_filename=root + "/docs/gen/ex_STMTDLY_faulty.rst", lines="10") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_STMTDLY_msg.rst", + out_filename=root + "/docs/gen/ex_STMTDLY_msg.rst", lines="1") test.passes() diff --git a/test_regress/t/t_lint_widthexpand_docs_bad.py b/test_regress/t/t_lint_widthexpand_docs_bad.py index 2b57b2bd5..6d7a81fa7 100755 --- a/test_regress/t/t_lint_widthexpand_docs_bad.py +++ b/test_regress/t/t_lint_widthexpand_docs_bad.py @@ -21,16 +21,16 @@ test.lint(verilator_flags2=["--lint-only"], expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_WIDTHEXPAND_1_faulty.rst", + out_filename=root + "/docs/gen/ex_WIDTHEXPAND_1_faulty.rst", lines="8-10") test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_WIDTHEXPAND_1_msg.rst", + out_filename=root + "/docs/gen/ex_WIDTHEXPAND_1_msg.rst", lineno_adjust=-7, regexp=r'Warning-WIDTH') test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_WIDTHEXPAND_1_fixed.rst", + out_filename=root + "/docs/gen/ex_WIDTHEXPAND_1_fixed.rst", lines="18") test.passes() diff --git a/test_regress/t/t_var_bad_hide_docs.py b/test_regress/t/t_var_bad_hide_docs.py index fea969814..837cb2dc9 100755 --- a/test_regress/t/t_var_bad_hide_docs.py +++ b/test_regress/t/t_var_bad_hide_docs.py @@ -21,11 +21,11 @@ test.lint(verilator_flags2=["--lint-only -Wwarn-VARHIDDEN"], expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, - out_filename="../docs/gen/ex_VARHIDDEN_faulty.rst", + out_filename=root + "/docs/gen/ex_VARHIDDEN_faulty.rst", regexp=r'(module t|integer|endmodule)') test.extract(in_filename=test.golden_filename, - out_filename="../docs/gen/ex_VARHIDDEN_msg.rst", + out_filename=root + "/docs/gen/ex_VARHIDDEN_msg.rst", lineno_adjust=-6, regexp=r'(var_bad_hide)') From 3b8d10cae5caf16b29bc31c690e143813b45d823 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 2 May 2025 07:26:56 -0400 Subject: [PATCH 019/211] Commentary --- docs/guide/warnings.rst | 24 ++++++++++++------------ docs/spelling.txt | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 9e03d38d2..0a97c30e1 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1463,15 +1463,7 @@ List Of Warnings than zero, when it is to be used in a preprocessor expression. -.. option:: PROCASSWIRE - - .. TODO better example - - An error that a procedural assignment is setting a wire. According to IEEE, - a var/reg must be used as the target of procedural assignments. - - -.. option:: PROCINITASSIGN +.. option:: PROCASSINIT Warns that the specified signal is given an initial value where it is declared, and is also driven in an always process. Typically such @@ -1480,15 +1472,15 @@ List Of Warnings Faulty example: - .. include:: ../../docs/gen/ex_PROCINITASSIGN_faulty.rst + .. include:: ../../docs/gen/ex_PROCASSINIT_faulty.rst Results in: - .. include:: ../../docs/gen/ex_PROCINITASSIGN_msg.rst + .. include:: ../../docs/gen/ex_PROCASSINIT_msg.rst One possible fix, adding a reset to the always: - .. include:: ../../docs/gen/ex_PROCINITASSIGN_fixed.rst + .. include:: ../../docs/gen/ex_PROCASSINIT_fixed.rst Alternatively, use an initial block for the initialization: @@ -1500,6 +1492,14 @@ List Of Warnings correctly. +.. option:: PROCASSWIRE + + .. TODO better example + + An error that a procedural assignment is setting a wire. According to IEEE, + a var/reg must be used as the target of procedural assignments. + + .. option:: PROFOUTOFDATE Warns that threads were scheduled using estimated costs, even though diff --git a/docs/spelling.txt b/docs/spelling.txt index d2326c6eb..fe6ee5665 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -273,6 +273,7 @@ Mednick Mei Melo Menküc +Menon Michail Michiels Microsystems @@ -303,6 +304,7 @@ Nauticus Newgard Nigam Nikana +Niraj Niranjan Nitza Noack From 15ebbd309f72639cf0af0bf57e7adee410f5b1e6 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 2 May 2025 07:36:42 -0400 Subject: [PATCH 020/211] Fix always processes ignoring $finish (#5971). --- Changes | 1 + src/V3EmitCMain.cpp | 4 ++-- src/V3Sched.cpp | 10 ++++++-- test_regress/t/t_timing_debug1.out | 3 --- test_regress/t/t_timing_finish2.py | 18 ++++++++++++++ test_regress/t/t_timing_finish2.v | 33 ++++++++++++++++++++++++++ test_regress/t/t_timing_trace.out | 3 +-- test_regress/t/t_timing_trace_fst.out | 5 ++-- test_regress/t/t_timing_trace_saif.out | 4 ++-- test_regress/t/t_trace_timing1.out | 2 +- 10 files changed, 68 insertions(+), 15 deletions(-) create mode 100755 test_regress/t/t_timing_finish2.py create mode 100644 test_regress/t/t_timing_finish2.v diff --git a/Changes b/Changes index 786656ca0..5e407d7a1 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.037 devel * Fix filename backslash escapes in C code (#5947). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix constant propagation making upper bits Xs (#5969). +* Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] Verilator 5.036 2025-04-27 diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 985a9cea9..2abdcd70b 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -77,7 +77,7 @@ private: puts("\n"); puts("// Simulate until $finish\n"); - puts("while (!contextp->gotFinish()) {\n"); + puts("while (VL_LIKELY(!contextp->gotFinish())) {\n"); puts(/**/ "// Evaluate model\n"); puts(/**/ "topp->eval();\n"); puts(/**/ "// Advance time\n"); @@ -93,7 +93,7 @@ private: puts("}\n"); puts("\n"); - puts("if (!contextp->gotFinish()) {\n"); + puts("if (VL_LIKELY(!contextp->gotFinish())) {\n"); puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n"); puts("}\n"); puts("\n"); diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index a0a7b82dd..8b2b12629 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -435,8 +435,14 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) { if (VN_IS(procp, Always)) { subFuncp->slow(false); FileLine* const flp = procp->fileline(); - bodyp - = new AstWhile{flp, new AstConst{flp, AstConst::BitTrue{}}, bodyp}; + bodyp = new AstWhile{ + flp, + // If we change to use exceptions to handle finish/stop, + // this can get removed + new AstCExpr{flp, + "VL_LIKELY(!vlSymsp->_vm_contextp__->gotFinish())", 1, + true}, + bodyp}; } } subFuncp->addStmtsp(bodyp); diff --git a/test_regress/t/t_timing_debug1.out b/test_regress/t/t_timing_debug1.out index 0d1f5f568..eefec27ca 100644 --- a/test_regress/t/t_timing_debug1.out +++ b/test_regress/t/t_timing_debug1.out @@ -2354,7 +2354,6 @@ *-* All Finished *-* -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:10 -V{t#,#} Resuming: Process waiting at t/t_timing_sched.v:50 --V{t#,#} Suspending process waiting for @(posedge t.clk2) at t/t_timing_sched.v:50 -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_comb__TOP__0 -V{t#,#}+ Vt_timing_debug1___024root___eval_phase__act @@ -2362,8 +2361,6 @@ -V{t#,#}+ Vt_timing_debug1___024root___dump_triggers__act -V{t#,#} 'act' region trigger index 0 is active: @([hybrid] t.clk1) -V{t#,#}+ Vt_timing_debug1___024root___timing_commit --V{t#,#} Committing processes waiting for @(posedge t.clk2): --V{t#,#} - Process waiting at t/t_timing_sched.v:50 -V{t#,#}+ Vt_timing_debug1___024root___timing_resume -V{t#,#}+ Vt_timing_debug1___024root___eval_act -V{t#,#}+ Vt_timing_debug1___024root___act_sequent__TOP__0 diff --git a/test_regress/t/t_timing_finish2.py b/test_regress/t/t_timing_finish2.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_timing_finish2.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']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_timing_finish2.v b/test_regress/t/t_timing_finish2.v new file mode 100644 index 000000000..c252cf453 --- /dev/null +++ b/test_regress/t/t_timing_finish2.v @@ -0,0 +1,33 @@ +// 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 + +`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); + +module tb2 (); + parameter CLK_PERIOD = 2; + + reg clk = 1'b0; + int messages; + + always #(CLK_PERIOD / 2) clk = ~clk; + + always begin + int counter = 0; + while (counter < 3) begin + counter += 1; + $display("[%0t] Running loop %0d", $time, counter); + messages += 1; + @(posedge clk); + end + + $write("[%0t] *-* All Finished *-*\n", $time); + $finish; + end + + final `checkd(messages, 3); + +endmodule diff --git a/test_regress/t/t_timing_trace.out b/test_regress/t/t_timing_trace.out index f5a47ca02..504e3a2bc 100644 --- a/test_regress/t/t_timing_trace.out +++ b/test_regress/t/t_timing_trace.out @@ -83,6 +83,5 @@ b00000000000000000000000000000101 + #95 1( #100 -1% -1' 0( +0) diff --git a/test_regress/t/t_timing_trace_fst.out b/test_regress/t/t_timing_trace_fst.out index b95eb3cdb..fe5088ccd 100644 --- a/test_regress/t/t_timing_trace_fst.out +++ b/test_regress/t/t_timing_trace_fst.out @@ -1,5 +1,5 @@ $date - Sun Sep 22 22:53:52 2024 + Fri May 2 07:32:42 2025 $end $version @@ -92,5 +92,4 @@ $end 1$ #100 0$ -1) -1' +0& diff --git a/test_regress/t/t_timing_trace_saif.out b/test_regress/t/t_timing_trace_saif.out index eacb5a045..8b2e73be3 100644 --- a/test_regress/t/t_timing_trace_saif.out +++ b/test_regress/t/t_timing_trace_saif.out @@ -77,8 +77,8 @@ (rst (T0 0) (T1 100) (TZ 0) (TX 0) (TB 0) (TC 1)) (clk (T0 50) (T1 50) (TZ 0) (TX 0) (TB 0) (TC 20)) (a (T0 100) (T1 0) (TZ 0) (TX 0) (TB 0) (TC 0)) - (b (T0 0) (T1 100) (TZ 0) (TX 0) (TB 0) (TC 1)) - (c (T0 50) (T1 50) (TZ 0) (TX 0) (TB 0) (TC 11)) + (b (T0 0) (T1 100) (TZ 0) (TX 0) (TB 0) (TC 2)) + (c (T0 50) (T1 50) (TZ 0) (TX 0) (TB 0) (TC 10)) (d (T0 100) (T1 0) (TZ 0) (TX 0) (TB 0) (TC 0)) (ev (T0 100) (T1 0) (TZ 0) (TX 0) (TB 0) (TC 0)) ) diff --git a/test_regress/t/t_trace_timing1.out b/test_regress/t/t_trace_timing1.out index b829e1aca..9aa36ef0b 100644 --- a/test_regress/t/t_trace_timing1.out +++ b/test_regress/t/t_trace_timing1.out @@ -22,4 +22,4 @@ b00000000000000000000000000001010 % #15 1$ #20 -1# +0$ From 1a1c6e8797c4b998d09fea97feb4b8fa13f140df Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 3 May 2025 04:00:47 -0400 Subject: [PATCH 021/211] Change cell messages to instance to match IEEE --- docs/gen/ex_PINMISSING_msg.rst | 2 +- examples/json_py/vl_hier_graph | 8 ++++---- src/V3Inline.cpp | 4 ++-- src/V3LinkCells.cpp | 6 +++--- src/V3LinkDot.cpp | 2 +- src/V3TraceDecl.cpp | 2 +- test_regress/t/t_inst_missing_bad.out | 4 ++-- test_regress/t/t_inst_pin_place_bad.out | 2 +- test_regress/t/t_lint_pindup_bad.out | 2 +- test_regress/t/t_lint_pinmissing_bad.out | 2 +- test_regress/t/t_udp_bad.out | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/gen/ex_PINMISSING_msg.rst b/docs/gen/ex_PINMISSING_msg.rst index c1a06525e..eef3cdb1a 100644 --- a/docs/gen/ex_PINMISSING_msg.rst +++ b/docs/gen/ex_PINMISSING_msg.rst @@ -1,4 +1,4 @@ .. comment: generated by t_lint_pinmissing_bad .. code-block:: - %Warning-PINMISSING: example.v:1:8 Cell has missing pin: 'port' + %Warning-PINMISSING: example.v:1:8 Instance has missing pin: 'port' diff --git a/examples/json_py/vl_hier_graph b/examples/json_py/vl_hier_graph index 6070768b3..c38520816 100755 --- a/examples/json_py/vl_hier_graph +++ b/examples/json_py/vl_hier_graph @@ -58,11 +58,11 @@ class VlHierGraph: top_module = False fh.write("];\n") - cells = self.flatten(mod, lambda n: n['type'] == "CELL") - for cell in cells: - def_number = self.addr_to_vertex_number(cell['modp']) + instances = self.flatten(mod, lambda n: n['type'] == "CELL") + for inst in instances: + def_number = self.addr_to_vertex_number(inst['modp']) fh.write(" n%d->n%d [label=\"%s\"];\n" % - (mod_number, def_number, cell['name'])) + (mod_number, def_number, inst['name'])) fh.write("}\n") diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index a7092fbb0..53081e2a7 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -664,8 +664,8 @@ void V3Inline::inlineAll(AstNetlist* nodep) { for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = VN_AS(modp->nextp(), NodeModule)) { UASSERT_OBJ(!moduleState(modp).m_inlined, modp, - "Inlined module should have been deleted when the last cell referencing " - "it was inlined"); + "Inlined module should have been deleted when the last instance " + "referencing it was inlined"); } } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index f1cf0deb3..ed24b42d7 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -410,10 +410,10 @@ class LinkCellsVisitor final : public VNVisitor { if (!pinp->exprp()) { if (pinp->name().substr(0, 11) == "__pinNumber") { pinp->v3warn(PINNOCONNECT, - "Cell pin is not connected: " << pinp->prettyNameQ()); + "Instance pin is not connected: " << pinp->prettyNameQ()); } else { pinp->v3warn(PINCONNECTEMPTY, - "Cell pin connected by name with empty reference: " + "Instance pin connected by name with empty reference: " << pinp->prettyNameQ()); } } @@ -471,7 +471,7 @@ class LinkCellsVisitor final : public VNVisitor { nodep->addPinsp(newp); } else { nodep->v3warn(PINMISSING, - "Cell has missing pin: " + "Instance has missing pin: " << portp->prettyNameQ() << '\n' << nodep->warnContextPrimary() << '\n' << portp->warnOther() diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 683bc3a50..39e8d4510 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2696,7 +2696,7 @@ class LinkDotResolveVisitor final : public VNVisitor { VL_RESTORER(m_usedPins); m_usedPins.clear(); UASSERT_OBJ(nodep->modp(), nodep, - "Cell has unlinked module"); // V3LinkCell should have errored out + "Instance has unlinked module"); // V3LinkCell should have errored out VL_RESTORER(m_cellp); VL_RESTORER(m_pinSymp); { diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index ff7d92f4b..1b4afbf44 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -413,7 +413,7 @@ class TraceDeclVisitor final : public VNVisitor { // This is a subscope: insert a placeholder to be fixed up later AstCell* const cellp = entry.cellp(); AstNodeStmt* const stmtp = new AstComment{ - cellp->fileline(), "Cell init for: " + cellp->prettyName()}; + cellp->fileline(), "Instance init for: " + cellp->prettyName()}; addToSubFunc(stmtp); m_cellInitPlaceholders.emplace_back(nodep, cellp, stmtp); } diff --git a/test_regress/t/t_inst_missing_bad.out b/test_regress/t/t_inst_missing_bad.out index 2bbd2390d..8407aaff4 100644 --- a/test_regress/t/t_inst_missing_bad.out +++ b/test_regress/t/t_inst_missing_bad.out @@ -1,9 +1,9 @@ -%Warning-PINNOCONNECT: t/t_inst_missing_bad.v:13:17: Cell pin is not connected: '__pinNumber2' +%Warning-PINNOCONNECT: t/t_inst_missing_bad.v:13:17: Instance pin is not connected: '__pinNumber2' 13 | sub sub (ok, , nc); | ^ ... For warning description see https://verilator.org/warn/PINNOCONNECT?v=latest ... Use "/* verilator lint_off PINNOCONNECT */" and lint_on around source to disable this message. -%Warning-PINMISSING: t/t_inst_missing_bad.v:13:8: Cell has missing pin: 'missing' +%Warning-PINMISSING: t/t_inst_missing_bad.v:13:8: Instance has missing pin: 'missing' 13 | sub sub (ok, , nc); | ^~~ t/t_inst_missing_bad.v:16:51: ... Location of port declaration diff --git a/test_regress/t/t_inst_pin_place_bad.out b/test_regress/t/t_inst_pin_place_bad.out index dd548becf..0a305f913 100644 --- a/test_regress/t/t_inst_pin_place_bad.out +++ b/test_regress/t/t_inst_pin_place_bad.out @@ -1,4 +1,4 @@ -%Warning-PINMISSING: t/t_inst_pin_place_bad.v:21:7: Cell has missing pin: 'pin_1' +%Warning-PINMISSING: t/t_inst_pin_place_bad.v:21:7: Instance has missing pin: 'pin_1' 21 | ) i_sub ( | ^~~~~ t/t_inst_pin_place_bad.v:11:11: ... Location of port declaration diff --git a/test_regress/t/t_lint_pindup_bad.out b/test_regress/t/t_lint_pindup_bad.out index b758f052c..ef1bb3d7f 100644 --- a/test_regress/t/t_lint_pindup_bad.out +++ b/test_regress/t/t_lint_pindup_bad.out @@ -1,4 +1,4 @@ -%Warning-PINMISSING: t/t_lint_pindup_bad.v:18:4: Cell has missing pin: 'exists' +%Warning-PINMISSING: t/t_lint_pindup_bad.v:18:4: Instance has missing pin: 'exists' 18 | sub (.o(o), | ^~~ t/t_lint_pindup_bad.v:32:15: ... Location of port declaration diff --git a/test_regress/t/t_lint_pinmissing_bad.out b/test_regress/t/t_lint_pinmissing_bad.out index c86daaab5..7a57cda41 100644 --- a/test_regress/t/t_lint_pinmissing_bad.out +++ b/test_regress/t/t_lint_pinmissing_bad.out @@ -1,4 +1,4 @@ -%Warning-PINMISSING: t/t_lint_pinmissing_bad.v:8:8: Cell has missing pin: 'port' +%Warning-PINMISSING: t/t_lint_pinmissing_bad.v:8:8: Instance has missing pin: 'port' 8 | sub sub(); | ^~~ t/t_lint_pinmissing_bad.v:11:11: ... Location of port declaration diff --git a/test_regress/t/t_udp_bad.out b/test_regress/t/t_udp_bad.out index 3438ee663..e630d303f 100644 --- a/test_regress/t/t_udp_bad.out +++ b/test_regress/t/t_udp_bad.out @@ -1,4 +1,4 @@ -%Warning-PINMISSING: t/t_udp_bad.v:10:10: Cell has missing pin: 'c_bad' +%Warning-PINMISSING: t/t_udp_bad.v:10:10: Instance has missing pin: 'c_bad' 10 | udp_x x (a, b); | ^ t/t_udp_bad.v:14:28: ... Location of port declaration From e837f780a22210294e8088e27e6939eecfc986fd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 3 May 2025 04:25:01 -0400 Subject: [PATCH 022/211] Commentary --- docs/guide/exe_verilator.rst | 34 ++++++++++++++++++---------------- docs/guide/extensions.rst | 7 ++++--- docs/guide/languages.rst | 2 +- docs/guide/verilating.rst | 2 +- docs/guide/warnings.rst | 2 +- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index c75d21f3a..3024d07f0 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1,9 +1,9 @@ .. Copyright 2003-2025 by Wilson Snyder. .. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -===================== - verilator Arguments -===================== +=================== +verilator Arguments +=================== The following arguments may be passed to the "verilator" executable. @@ -1774,7 +1774,7 @@ Summary: them systematically. The generated file is in the Verilator Configuration format, see - :ref:`Configuration Files`. The standard file extension is ".vlt". + :ref:`Verilator Configuration Files`. The standard file extension is ".vlt". These files can directly be consumed by Verilator, typically by placing the filename as part of the Verilator command line options. Waiver files need to be listed on the command line before listing the files they are @@ -2015,18 +2015,20 @@ Summary: filenames. -.. _Configuration Files: +.. _Verilator Configuration Files: -===================== - Configuration Files -===================== +============================= +Verilator Configuration Files +============================= In addition to the command line, warnings and other features for the -:command:`verilator` command may be controlled with configuration files, -typically named with the `.vlt` extension (what makes it a configuration -file is the :option:`\`verilator_config` directive). These files, when -named `.vlt`, are read before source code files; if this behavior is -undesired, name the config file with a `.v` suffix. +:command:`verilator` command may be controlled with Verilator Configuration +Files, not to be confused with IEEE Configurations blocks +(`config...endconfig`) inside a file. Typically named with the `.vlt` +extension, what makes it a Verilator Configuration File is the +:option:`\`verilator_config` directive. These files, when named `.vlt`, +are read before source code files; if this behavior is undesired, name the +config file with a `.v` suffix. An example: @@ -2038,9 +2040,9 @@ An example: This disables WIDTH warnings globally, and CASEX for a specific file. -Configuration files are fed through the normal Verilog preprocessor prior -to parsing, so "\`ifdef", "\`define", and comments may be used as if the -configuration file was standard Verilog code. +Verilator configuration files are fed through the normal Verilog +preprocessor prior to parsing, so "\`ifdef", "\`define", and comments may +be used as if the configuration file was standard Verilog code. Note that file or line-specific configuration only applies to files read after the configuration file. It is therefore recommended to pass the diff --git a/docs/guide/extensions.rst b/docs/guide/extensions.rst index b01c28f91..1d54ca0b2 100644 --- a/docs/guide/extensions.rst +++ b/docs/guide/extensions.rst @@ -30,7 +30,7 @@ or "`ifdef`"'s may break other tools. Specifies the entire begin/end block should be ignored for coverage analysis. Must be inside a code block, e.g., within a begin/end pair. - Same as :option:`coverage_block_off` in :ref:`Configuration Files`. + Same as :option:`coverage_block_off` in :ref:`Verilator Configuration Files`. .. option:: `error [string] @@ -135,8 +135,9 @@ or "`ifdef`"'s may break other tools. .. option:: `verilator_config - Take the remaining text up to the next :option:`\`verilog` mode switch and - treat it as Verilator configuration commands. See :ref:`Configuration Files`. + Take the remaining text up to the next :option:`\`verilog` mode switch + and treat it as Verilator configuration commands. See :ref:`Verilator + Configuration Files`. .. option:: `VERILATOR_TIMING diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index a13c11184..f21a2666b 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -6,7 +6,7 @@ Input Languages *************** This section describes the languages Verilator takes as input. See also -:ref:`Configuration Files`. +:ref:`Verilator Configuration Files`. Language Standard Support diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index 058caa52f..9b0d3f15b 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -94,7 +94,7 @@ There are two ways to mark a module: * Write :option:`/*verilator&32;hier_block*/` metacomment in HDL code. -* Add a :option:`hier_block` line in the :ref:`Configuration Files`. +* Add a :option:`hier_block` line in the :ref:`Verilator Configuration Files`. Then pass the :vlopt:`--hierarchical` option to Verilator. diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 0a97c30e1..bafd1827f 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -37,7 +37,7 @@ Warnings may be disabled in multiple ways: propagate upwards to any parent file (file that included the file with the lint_off). -#. Disable the warning using :ref:`Configuration Files` with a +#. Disable the warning using :ref:`Verilator Configuration Files` with a :option:`lint_off` command. This is useful when a script suppresses warnings, and the Verilog source should not be changed. This method also allows matching on the warning text. From ea65bcd86b1eaaf34210a3673d57c9bad7696bab Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 3 May 2025 05:30:40 -0400 Subject: [PATCH 023/211] Add lib.map information to unsupported message, etc --- docs/guide/languages.rst | 2 +- src/verilog.l | 23 ++++++++-------- src/verilog.y | 32 ++++++++++++++++++++++ test_regress/t/t_config_include_bad.out | 3 ++- test_regress/t/t_config_include_bad.py | 4 +-- test_regress/t/t_config_libmap.map | 17 ++++++++++++ test_regress/t/t_config_libmap.out | 29 ++++++++++++++++++++ test_regress/t/t_config_libmap.py | 18 +++++++++++++ test_regress/t/t_config_libmap.v | 12 +++++++++ test_regress/t/t_config_libmap_inc.map | 17 ++++++++++++ test_regress/t/t_dist_copyright.py | 2 +- test_regress/t/t_lint_rsvd_bad.out | 36 +++++++++++-------------- 12 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 test_regress/t/t_config_libmap.map create mode 100644 test_regress/t/t_config_libmap.out create mode 100755 test_regress/t/t_config_libmap.py create mode 100644 test_regress/t/t_config_libmap.v create mode 100644 test_regress/t/t_config_libmap_inc.map diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index f21a2666b..62c50b706 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -536,4 +536,4 @@ $test$plusargs, $value$plusargs {VerilatedContext*} ->commandArgs(argc, argv); to register the command line before calling $test$plusargs or - $value$plusargs. + $value$plusargs. Or use :vlopt:`--binary` or :vlopt:`--main`. diff --git a/src/verilog.l b/src/verilog.l index add1df161..722ed6f3b 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -441,18 +441,19 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* Verilog 2001 Config */ { /* Generic unsupported keywords */ - "cell" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "config" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "design" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "endconfig" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "incdir" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "include" { FL; yylval.fl->v3warn(E_UNSUPPORTED, "Unsupported: Verilog 2001-config reserved word not implemented;" - " suggest you want `include instead: '" << yytext << "'"); + "cell" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "config" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "design" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "endconfig" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "incdir" { FL; ERROR_RSVD_WORD("Verilog 2001-config lib.map"); } + "include" { FL; yylval.fl->v3warn(E_UNSUPPORTED, "Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include'\n" + << yylval.fl->warnMore() << "... Suggest unless in a lib.map file," + " want `include instead"); FL_BRK; } - "instance" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "liblist" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "library" { ERROR_RSVD_WORD("Verilog 2001-config"); } - "use" { ERROR_RSVD_WORD("Verilog 2001-config"); } + "instance" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "liblist" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } + "library" { FL; ERROR_RSVD_WORD("Verilog 2001-config lib.map"); } + "use" { FL; ERROR_RSVD_WORD("Verilog 2001-config"); } } /* Verilog 2005 */ diff --git a/src/verilog.y b/src/verilog.y index f88c4c94a..ebe90b364 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7733,6 +7733,38 @@ colon: // Generic colon that isn't making a label (e.g. | yP_COLON__FORK { $$ = $1; } ; +//********************************************************************** +// Config - config...endconfig + +//********************************************************************** +// Config - lib.map + +//UNSUP library_text: // == IEEE: library_text (note is top-level entry point) +//UNSUP library_description { } +//UNSUP | library_text library_description { } +//UNSUP ; + +//UNSUP library_description: // == IEEE: library_description +//UNSUP // // IEEE: library_declaration +//UNSUP yLIBRARY idAny/*library_identifier*/ file_path_specList ';' +//UNSUP { BBUNSUP($1, "Unsupported: config lib.map library"); } +//UNSUP yLIBRARY idAny/*library_identifier*/ file_path_specList '-' yINCDIR file_path_specList ';' +//UNSUP { BBUNSUP($1, "Unsupported: config lib.map library"); } +//UNSUP // // IEEE: include_statement +//UNSUP | yINCLUDE file_path_spec ';' { BBUNSUP($1, "Unsupported: config include"); } +//UNSUP | config_declaration { } +//UNSUP | ';' { } +//UNSUP ; + +//UNSUP file_path_specList: // IEEE: file_path_spec { ',' file_path_spec } +//UNSUP file_path_spec { } +//UNSUP | file_path_specList ',' file_path_spec { } +//UNSUP ; + +//UNSUP file_path_spec: // IEEE: file_path_spec +//UNSUP Needs to be lexer rule, Note '/' '*' must not be a comment. +//UNSUP ; + //********************************************************************** // VLT Files diff --git a/test_regress/t/t_config_include_bad.out b/test_regress/t/t_config_include_bad.out index b7cc607aa..c076f0ee5 100644 --- a/test_regress/t/t_config_include_bad.out +++ b/test_regress/t/t_config_include_bad.out @@ -1,4 +1,5 @@ -%Error-UNSUPPORTED: t/t_config_include_bad.v:7:1: Unsupported: Verilog 2001-config reserved word not implemented; suggest you want `include instead: 'include' +%Error-UNSUPPORTED: t/t_config_include_bad.v:7:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' + : ... Suggest unless in a lib.map file, want `include instead 7 | include "meant_to_tick_include.v" | ^~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest diff --git a/test_regress/t/t_config_include_bad.py b/test_regress/t/t_config_include_bad.py index acbdae169..cf71fd2b8 100755 --- a/test_regress/t/t_config_include_bad.py +++ b/test_regress/t/t_config_include_bad.py @@ -11,8 +11,6 @@ import vltest_bootstrap test.scenarios('linter') -test.lint(verilator_flags2=["--lint-only -Wwarn-REALCVT"], - fails=True, - expect_filename=test.golden_filename) +test.lint(verilator_flags2=["--lint-only"], fails=True, expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_config_libmap.map b/test_regress/t/t_config_libmap.map new file mode 100644 index 000000000..fe1f6e3f2 --- /dev/null +++ b/test_regress/t/t_config_libmap.map @@ -0,0 +1,17 @@ +// 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 + +// lib.map file: +include ./t_config_libmap_inc.map + +library rtllib *.v; +library rtllib2 *.v, *.sv; +library rtllib3 *.v -incdir *.vh; +library rtllib4 *.v -incdir *.vh, *.svh; + +config cfg; + design t; +endconfig diff --git a/test_regress/t/t_config_libmap.out b/test_regress/t/t_config_libmap.out new file mode 100644 index 000000000..d86495d89 --- /dev/null +++ b/test_regress/t/t_config_libmap.out @@ -0,0 +1,29 @@ +%Error-UNSUPPORTED: t/t_config_libmap.map:8:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' + : ... Suggest unless in a lib.map file, want `include instead + 8 | include ./t_config_libmap_inc.map + | ^~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_config_libmap.map:8:9: syntax error, unexpected '.' + 8 | include ./t_config_libmap_inc.map + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_config_libmap.map:10:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 10 | library rtllib *.v; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:11:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 11 | library rtllib2 *.v, *.sv; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:12:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 12 | library rtllib3 *.v -incdir *.vh; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:12:29: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'incdir' +%Error-UNSUPPORTED: t/t_config_libmap.map:13:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 13 | library rtllib4 *.v -incdir *.vh, *.svh; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:13:29: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'incdir' + 13 | library rtllib4 *.v -incdir *.vh, *.svh; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:15:1: Unsupported: Verilog 2001-config reserved word not implemented: 'config' +%Error-UNSUPPORTED: t/t_config_libmap.map:16:4: Unsupported: Verilog 2001-config reserved word not implemented: 'design' +%Error-UNSUPPORTED: t/t_config_libmap.map:17:1: Unsupported: Verilog 2001-config reserved word not implemented: 'endconfig' +%Error: Exiting due to diff --git a/test_regress/t/t_config_libmap.py b/test_regress/t/t_config_libmap.py new file mode 100755 index 000000000..14bcdb8d6 --- /dev/null +++ b/test_regress/t/t_config_libmap.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('linter') + +test.lint(verilator_flags2=["--lint-only", "t/" + test.name + ".map"], + fails=test.vlt_all, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_config_libmap.v b/test_regress/t/t_config_libmap.v new file mode 100644 index 000000000..7bb33e757 --- /dev/null +++ b/test_regress/t/t_config_libmap.v @@ -0,0 +1,12 @@ +// 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; + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_config_libmap_inc.map b/test_regress/t/t_config_libmap_inc.map new file mode 100644 index 000000000..fe1f6e3f2 --- /dev/null +++ b/test_regress/t/t_config_libmap_inc.map @@ -0,0 +1,17 @@ +// 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 + +// lib.map file: +include ./t_config_libmap_inc.map + +library rtllib *.v; +library rtllib2 *.v, *.sv; +library rtllib3 *.v -incdir *.vh; +library rtllib4 *.v -incdir *.vh, *.svh; + +config cfg; + design t; +endconfig diff --git a/test_regress/t/t_dist_copyright.py b/test_regress/t/t_dist_copyright.py index 76ef97b24..12c1a4c3d 100755 --- a/test_regress/t/t_dist_copyright.py +++ b/test_regress/t/t_dist_copyright.py @@ -12,7 +12,7 @@ import datetime test.scenarios('dist') -RELEASE_OK_RE = r'(^test_regress/t/.*\.(cpp|h|mk|sv|v|vlt)|^test_regress/t_done/|^examples/)' +RELEASE_OK_RE = r'(^test_regress/t/.*\.(cpp|h|map|mk|sv|v|vlt)|^test_regress/t_done/|^examples/)' EXEMPT_AUTHOR_RE = r'(^ci/|^nodist/fastcov.py|^nodist/fuzzer|^test_regress/t/.*\.(cpp|h|v|vlt)$)' diff --git a/test_regress/t/t_lint_rsvd_bad.out b/test_regress/t/t_lint_rsvd_bad.out index 31cfb628d..c98cecae5 100644 --- a/test_regress/t/t_lint_rsvd_bad.out +++ b/test_regress/t/t_lint_rsvd_bad.out @@ -1,42 +1,36 @@ %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:7:1: Unsupported: Verilog 2001-config reserved word not implemented: 'config' 7 | config cfgBad; - | ^~~~~~ + | ^~~~~~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_lint_rsvd_bad.v:7:8: syntax error, unexpected IDENTIFIER - 7 | config cfgBad; - | ^~~~~~ +%Error: t/t_lint_rsvd_bad.v:7:14: syntax error, unexpected IDENTIFIER ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:8:4: Unsupported: Verilog 2001-config reserved word not implemented: 'design' 8 | design rtlLib.top; - | ^~~~~~ + | ^~~~~~~~~~~~ %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:9:12: Unsupported: Verilog 2001-config reserved word not implemented: 'liblist' 9 | default liblist rtlLib; - | ^~~~~~~ + | ^~~~~~~~~~~~~~ %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:10:4: Unsupported: Verilog 2001-config reserved word not implemented: 'instance' 10 | instance top.a2 liblist gateLib; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:10:20: Unsupported: Verilog 2001-config reserved word not implemented: 'liblist' - 10 | instance top.a2 liblist gateLib; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:11:4: Unsupported: Verilog 2001-config reserved word not implemented; suggest you want `include instead: 'include' + | ^~~~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:10:28: Unsupported: Verilog 2001-config reserved word not implemented: 'liblist' +%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:11:4: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' + : ... Suggest unless in a lib.map file, want `include instead 11 | include none; | ^~~~~~~ -%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:12:4: Unsupported: Verilog 2001-config reserved word not implemented: 'library' +%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:12:4: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' 12 | library rtlLib *.v; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:13:4: Unsupported: Verilog 2001-config reserved word not implemented; suggest you want `include instead: 'include' + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:13:4: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' + : ... Suggest unless in a lib.map file, want `include instead 13 | include aa; | ^~~~~~~ %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:14:4: Unsupported: Verilog 2001-config reserved word not implemented: 'use' 14 | use gateLib; - | ^~~ + | ^~~~~~ %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:15:4: Unsupported: Verilog 2001-config reserved word not implemented: 'cell' 15 | cell rtlLib.cell; - | ^~~~ -%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:15:16: Unsupported: Verilog 2001-config reserved word not implemented: 'cell' - 15 | cell rtlLib.cell; - | ^~~~ + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:15:20: Unsupported: Verilog 2001-config reserved word not implemented: 'cell' %Error-UNSUPPORTED: t/t_lint_rsvd_bad.v:16:1: Unsupported: Verilog 2001-config reserved word not implemented: 'endconfig' - 16 | endconfig - | ^~~~~~~~~ %Error: Exiting due to From 70c84d3abdb32801bce81a066e976e199af728c9 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 4 May 2025 16:31:27 +0100 Subject: [PATCH 024/211] Preserve C++ widths in V3Expand (#5975) During V3Expand, some w32/1 (width == 32, widthMin == 1), nodes (e.g.: RedOr) are replaced with w1 nodes (width == widthMin == 1) (e.g.: Neq). However, V3Expand runs after V3Clean, when we are in C++ width world, so we need to preserve the width/widthMin distinction, otherwise a later constant folding can eliminate e.g. a necessary AstAnd used clear an intermediate result (isAllOnes is true for a Const 1 with w1, but false for a Const 1 with w32/1). Attempting to fix by preserving all width/widthMin during a replacement in V3Expand. DFG itself is fine, but the transformed code hits the above. Fixes #5953 --- src/V3Expand.cpp | 5 ++ test_regress/t/t_opt_expand_keep_widths.out | 7 +++ test_regress/t/t_opt_expand_keep_widths.py | 18 ++++++ test_regress/t/t_opt_expand_keep_widths.v | 61 +++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 test_regress/t/t_opt_expand_keep_widths.out create mode 100755 test_regress/t/t_opt_expand_keep_widths.py create mode 100644 test_regress/t/t_opt_expand_keep_widths.v diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index fb348121b..395ba5e04 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -123,6 +123,11 @@ class ExpandVisitor final : public VNVisitor { } static void replaceWithDelete(AstNode* nodep, AstNode* newp) { newp->user1(1); // Already processed, don't need to re-iterate + if (newp->width() != nodep->width()) { + UASSERT_OBJ(newp->widthMin() == nodep->widthMin(), nodep, + "Replacement width mismatch"); + newp->dtypeChgWidth(nodep->width(), nodep->widthMin()); + } nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } diff --git a/test_regress/t/t_opt_expand_keep_widths.out b/test_regress/t/t_opt_expand_keep_widths.out new file mode 100644 index 000000000..f310ca87a --- /dev/null +++ b/test_regress/t/t_opt_expand_keep_widths.out @@ -0,0 +1,7 @@ +[0] in5=0 clock_10=0 clock_12=0 out18=1 +[5] in5=0 clock_10=0 clock_12=1 out18=1 +[10] in5=0 clock_10=0 clock_12=0 out18=1 +[15] in5=0 clock_10=1 clock_12=0 out18=1 +[15] in5=0 clock_10=1 clock_12=0 out18=0 +[20] in5=0 clock_10=0 clock_12=0 out18=0 +*-* All Finished *-* diff --git a/test_regress/t/t_opt_expand_keep_widths.py b/test_regress/t/t_opt_expand_keep_widths.py new file mode 100755 index 000000000..fa87f0e9d --- /dev/null +++ b/test_regress/t/t_opt_expand_keep_widths.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']) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_opt_expand_keep_widths.v b/test_regress/t/t_opt_expand_keep_widths.v new file mode 100644 index 000000000..a19565b00 --- /dev/null +++ b/test_regress/t/t_opt_expand_keep_widths.v @@ -0,0 +1,61 @@ +// 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 gymhnulbvj (in5, clock_10, clock_12, out18); + + input wire [23:22] in5; + wire [29:1] wire_4; + reg reg_35; + output wire out18; + input wire clock_10; + input wire clock_12; + + // verilator lint_off WIDTH + assign wire_4 = ~ in5[22]; + assign out18 = reg_35 ? 0 : !(!(~(wire_4[6:5] | 8'hc6))); + // verilator lint_on WIDTH + + always @(posedge clock_10 or posedge clock_12) begin + if (clock_12) begin + reg_35 <= 0; + end + else begin + // verilator lint_off WIDTH + reg_35 <= wire_4; + // verilator lint_on WIDTH + end + end +endmodule + +module t; + reg [23:22] in5; + reg clock_10 = 0; + reg clock_12 = 0; + wire out18; + + gymhnulbvj uut ( + .in5(in5), + .clock_10(clock_10), + .clock_12(clock_12), + .out18(out18) + ); + + initial begin + $monitor("[%0t] in5=%d clock_10=%d clock_12=%d out18=%d", $time, in5, clock_10, clock_12, out18); + + in5 = 2'b00; + #5 clock_12 = 1; + #5 clock_12 = 0; + + #5 clock_10 = 1; + #5 clock_10 = 0; + + #10; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 223bb9ba9add0b0f41d17045a2c12f50e3f8fe01 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 4 May 2025 19:28:51 +0100 Subject: [PATCH 025/211] Fix streaming to/from packed arrays (#5976) bug from 6bb57e463035a94fafaefe652e747303f9c548e0 Fixes RTLMeter OpenTitan. Fixes #5955. --- .github/workflows/rtlmeter.yml | 4 +- src/V3Width.cpp | 5 +- test_regress/t/t_stream5.v | 15 ++++-- test_regress/t/t_stream_unpack.v | 80 ++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index 187d9d60f..cb2e43edf 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -52,7 +52,7 @@ jobs: - "OpenPiton:1x1:*" - "OpenPiton:2x2:*" - "OpenPiton:4x4:*" - # - "OpenTitan:*" + - "OpenTitan:*" - "VeeR-EH1:asic*" - "VeeR-EH1:default*" - "VeeR-EH1:hiperf*" @@ -92,7 +92,7 @@ jobs: - "OpenPiton:1x1:*" - "OpenPiton:2x2:*" - "OpenPiton:4x4:*" - # - "OpenTitan:*" + - "OpenTitan:*" - "VeeR-EH1:asic*" - "VeeR-EH1:default*" - "VeeR-EH1:hiperf*" diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5e75473b2..ad749136d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5233,7 +5233,7 @@ class WidthVisitor final : public VNVisitor { << lwidth << " bits) is narrower than the stream (" << rwidth << " bits) (IEEE 1800-2023 11.4.14)"); } - if (VN_IS(lhsDTypeSkippedRefp, NodeArrayDType)) { + if (VN_IS(lhsDTypeSkippedRefp, UnpackArrayDType)) { streamp->unlinkFrBack(); nodep->rhsp(new AstCvtPackedToArray{streamp->fileline(), streamp, lhsDTypeSkippedRefp}); @@ -5241,7 +5241,6 @@ class WidthVisitor final : public VNVisitor { } if (const AstNodeStream* const streamp = VN_CAST(nodep->lhsp(), NodeStream)) { const AstNodeDType* const rhsDTypep = nodep->rhsp()->dtypep()->skipRefp(); - const int lwidth = widthUnpacked(streamp->lhsp()->dtypep()->skipRefp()); const int rwidth = widthUnpacked(rhsDTypep); if (rwidth != 0 && rwidth < lwidth) { @@ -5251,7 +5250,7 @@ class WidthVisitor final : public VNVisitor { << " bits, but source expression only provides " << rwidth << " bits (IEEE 1800-2023 11.4.14.3)"); } - if (VN_IS(rhsDTypep, NodeArrayDType)) { + if (VN_IS(rhsDTypep, UnpackArrayDType)) { AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); nodep->rhsp( new AstCvtArrayToPacked{rhsp->fileline(), rhsp, streamp->dtypep()}); diff --git a/test_regress/t/t_stream5.v b/test_regress/t/t_stream5.v index 1eab118d9..8ece51a8f 100644 --- a/test_regress/t/t_stream5.v +++ b/test_regress/t/t_stream5.v @@ -8,15 +8,21 @@ module t(/*AUTOARG*/); logic [15:0] i16; logic [15:0] o16; + logic [3:0][3:0] p16; logic [31:0] i32; logic [31:0] o32; + logic [7:0][3:0] p32; logic [63:0] i64; logic [63:0] o64; + logic [15:0][3:0] p64; always_comb begin o16 = {<<4{i16}}; + p16 = {<<4{i16}}; o32 = {<<4{i32}}; + p32 = {<<4{i32}}; o64 = {<<4{i64}}; + p64 = {<<4{i64}}; end initial begin @@ -24,12 +30,15 @@ module t(/*AUTOARG*/); i32 = 32'hcafefade; i64 = 64'hdeaddeedcafefade; #100ns; - $display("o16=0x%h i16=0x%h", o16, i16); + $display("o16=0x%h p16=0x%h i16=0x%h", o16, p16, i16); if (o16 != 16'hEDAF) $stop; - $display("o32=0x%h i32=0x%h", o32, i32); + if (p16 != 16'hEDAF) $stop; + $display("o32=0x%h p32=0x%h i32=0x%h", o32, p32, i32); if (o32 != 32'hEDAFEFAC) $stop; - $display("o64=0x%h i64=0x%h", o64, i64); + if (p32 != 32'hEDAFEFAC) $stop; + $display("o64=0x%h p64=0x%h i64=0x%h", o64, p64, i64); if (o64 != 64'hEDAFEFACDEEDDAED) $stop; + if (p64 != 64'hEDAFEFACDEEDDAED) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_stream_unpack.v b/test_regress/t/t_stream_unpack.v index 895b56a5f..fce34bb57 100644 --- a/test_regress/t/t_stream_unpack.v +++ b/test_regress/t/t_stream_unpack.v @@ -20,8 +20,10 @@ module t (/*AUTOARG*/); bit6_unpacked_t arr; bit [1:0] arr2[3]; bit6_t arr6[1]; + bit6_t [0:0] parr6; bit6_t bit6 = 6'b111000; bit [5:0] ans; + bit [2:0][1:0] ans_packed; enum_t ans_enum; logic [1:0] a [3] = {1, 0, 3}; logic [1:0] b [3] = {1, 2, 0}; @@ -42,6 +44,12 @@ module t (/*AUTOARG*/); { >> bit {ans}} = arr; `checkh(ans, bit6); + ans_packed = { >> bit {arr} }; + `checkh(ans_packed, bit6); + + { >> bit {ans_packed}} = arr; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ >> bit {arr} }); `checkh(ans_enum, bit6); @@ -57,6 +65,12 @@ module t (/*AUTOARG*/); { << bit {ans} } = arr; `checkh(ans, bit6); + ans_packed = { << bit {arr} }; + `checkh(ans_packed, bit6); + + { << bit {ans_packed} } = arr; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ << bit {arr} }); `checkh(ans_enum, bit6); @@ -72,6 +86,12 @@ module t (/*AUTOARG*/); { >> bit[1:0] {ans} } = arr2; `checkh(ans, bit6); + ans_packed = { >> bit[1:0] {arr2} }; + `checkh(ans_packed, bit6); + + { >> bit[1:0] {ans_packed} } = arr2; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ >> bit[1:0] {arr2} }); `checkh(ans_enum, bit6); @@ -84,6 +104,12 @@ module t (/*AUTOARG*/); { << bit[1:0] {ans} } = arr2; `checkh(ans, bit6); + ans_packed = { << bit[1:0] {arr2} }; + `checkh(ans_packed, bit6); + + { << bit[1:0] {ans_packed} } = arr2; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ << bit[1:0] {arr2} }); `checkh(ans_enum, bit6); @@ -99,6 +125,12 @@ module t (/*AUTOARG*/); { >> bit[5:0] {ans} } = arr6; `checkh(ans, bit6); + ans_packed = { >> bit[5:0] {arr6} }; + `checkh(ans_packed, bit6); + + { >> bit[5:0] {ans_packed} } = arr6; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ >> bit[5:0] {arr6} }); `checkh(ans_enum, bit6); @@ -114,9 +146,57 @@ module t (/*AUTOARG*/); { << bit[5:0] {ans} } = arr6; `checkh(ans, bit6); + ans_packed = { << bit[5:0] {arr6} }; + `checkh(ans_packed, bit6); + + { << bit[5:0] {ans_packed} } = arr6; + `checkh(ans_packed, bit6); + ans_enum = enum_t'({ << bit[5:0] {arr6} }); `checkh(ans_enum, bit6); + { >> bit [5:0] {parr6} } = bit6; + `checkh(parr6, bit6); + + parr6 = { >> bit [5:0] {bit6}}; + `checkh(parr6, bit6); + + ans = { >> bit[5:0] {parr6} }; + `checkh(ans, bit6); + + { >> bit[5:0] {ans} } = parr6; + `checkh(ans, bit6); + + ans_packed = { >> bit[5:0] {parr6} }; + `checkh(ans_packed, bit6); + + { >> bit[5:0] {ans_packed} } = parr6; + `checkh(ans_packed, bit6); + + ans_enum = enum_t'({ >> bit[5:0] {parr6} }); + `checkh(ans_enum, bit6); + + { << bit [5:0] {parr6} } = bit6; + `checkh(parr6, bit6); + + parr6 = { << bit [5:0] {bit6}}; + `checkh(parr6, bit6); + + ans = { << bit[5:0] {parr6} }; + `checkh(ans, bit6); + + { << bit[5:0] {ans} } = parr6; + `checkh(ans, bit6); + + ans_packed = { << bit[5:0] {parr6} }; + `checkh(ans_packed, bit6); + + { << bit[5:0] {ans_packed} } = parr6; + `checkh(ans_packed, bit6); + + ans_enum = enum_t'({ << bit[5:0] {parr6} }); + `checkh(ans_enum, bit6); + d = { >> {a, b, c}}; `checkh(d, 16'b0100110110001100); From 01e66ac3494981f546a9403468dd3a38952f0d16 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 14:49:44 -0400 Subject: [PATCH 026/211] Commentary: Changes update --- Changes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 5e407d7a1..49b7e80b6 100644 --- a/Changes +++ b/Changes @@ -16,9 +16,11 @@ Verilator 5.037 devel * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). +* Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore] +* Fix constant propagation making upper bits Xs (#5955) (#5969). * Fix sign extension of signed compared with unsigned case items (#5968). -* Fix constant propagation making upper bits Xs (#5969). * Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] +* Fix streaming to/from packed arrays (#5976). [Geza Lore] Verilator 5.036 2025-04-27 From 51616ecf2f13220296318fbe6bb30c67b539731b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 14:57:10 -0400 Subject: [PATCH 027/211] Internals: Rename to instances, and other minor cleanups --- docs/guide/exe_verilator.rst | 2 +- src/V3Class.cpp | 2 +- src/V3ParseImp.cpp | 6 +- src/V3ParseImp.h | 4 +- src/verilog.y | 80 ++++++++++++++------------ test_regress/t/t_config_libmap.map | 5 ++ test_regress/t/t_config_libmap.out | 33 ++++++----- test_regress/t/t_config_libmap_inc.map | 1 + test_regress/t/t_param_type_bad.out | 2 +- 9 files changed, 75 insertions(+), 60 deletions(-) diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 3024d07f0..fad7ec37e 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -183,7 +183,7 @@ Summary: With :vlopt:`--clk`, the specified signal is marked as a clock signal. The provided signal name is specified using a RTL hierarchy path. For - example, v.foo.bar. If the signal is the input to top-module, then + example, v.foo.bar. If the signal is the input to the top-module, then directly provide the signal name. Alternatively, use a :option:`/*verilator&32;clocker*/` metacomment in RTL file to mark the signal directly. diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 656ce3db2..3139ded69 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -40,7 +40,7 @@ class ClassVisitor final : public VNVisitor { const VNUser1InUse m_inuser1; // MEMBERS - string m_prefix; // String prefix to add to name based on hier + string m_prefix; // String prefix to add to class name based on hier V3UniqueNames m_names; // For unique naming of structs and unions AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_classPackagep = nullptr; // Package moving into diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index d1686aaf9..d71d6a090 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -415,7 +415,7 @@ const V3ParseBisonYYSType* V3ParseImp::tokenPeekp(size_t depth) { return &m_tokensAhead.at(depth); } -size_t V3ParseImp::tokenPipeScanIdCell(size_t depthIn) { +size_t V3ParseImp::tokenPipeScanIdInst(size_t depthIn) { // Search around IEEE module_instantiation/interface_instantiation/program_instantiation // Return location of following token, or input if not found // yaID/*module_identifier*/ [ '#' '('...')' ] yaID/*name_of_instance*/ [ '['...']' ] '(' ... @@ -533,7 +533,7 @@ int V3ParseImp::tokenPipelineId(int token) { VL_RESTORER(yylval); // Remember value, as about to read ahead if (m_tokenLastBison.token != '@' && m_tokenLastBison.token != '#' && m_tokenLastBison.token != '.') { - if (const size_t depth = tokenPipeScanIdCell(0)) return yaID__aCELL; + if (const size_t depth = tokenPipeScanIdInst(0)) return yaID__aINST; } if (nexttok == '#') { // e.g. class_type parameter_value_assignment '::' const size_t depth = tokenPipeScanParam(0, false); @@ -757,7 +757,7 @@ std::ostream& operator<<(std::ostream& os, const V3ParseBisonYYSType& rhs) { if (rhs.token == yaID__ETC // || rhs.token == yaID__CC // || rhs.token == yaID__LEX // - || rhs.token == yaID__aCELL // + || rhs.token == yaID__aINST // || rhs.token == yaID__aTYPE) { os << " strp='" << *(rhs.strp) << "'"; } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 916bdb48c..e6ebcbc92 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -314,9 +314,9 @@ private: void tokenPipeline() VL_MT_DISABLED; // Internal; called from tokenToBison int tokenPipelineId(int token) VL_MT_DISABLED; void tokenPipelineSym() VL_MT_DISABLED; - size_t tokenPipeScanIdCell(size_t depth) VL_MT_DISABLED; + size_t tokenPipeScanIdInst(size_t depth) VL_MT_DISABLED; size_t tokenPipeScanBracket(size_t depth) VL_MT_DISABLED; - size_t tokenPipeScanParam(size_t depth, bool forCell) VL_MT_DISABLED; + size_t tokenPipeScanParam(size_t depth, bool forInst) VL_MT_DISABLED; size_t tokenPipeScanTypeEq(size_t depth) VL_MT_DISABLED; const V3ParseBisonYYSType* tokenPeekp(size_t depth) VL_MT_DISABLED; void preprocDumps(std::ostream& os, bool forInputs) VL_MT_DISABLED; diff --git a/src/verilog.y b/src/verilog.y index ebe90b364..126c2089c 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -430,7 +430,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yaID__ETC "IDENTIFIER" %token yaID__CC "IDENTIFIER-::" %token yaID__LEX "IDENTIFIER-in-lex" -%token yaID__aCELL "IDENTIFIER-for-cell" +%token yaID__aINST "IDENTIFIER-for-instance" %token yaID__aTYPE "IDENTIFIER-for-type" // Can't predecode aFUNCTION, can declare after use // Can't predecode aINTERFACE, can declare after use @@ -1444,7 +1444,7 @@ parameter_value_assignmentClassE: // IEEE: [ parameter_value_assignme ; parameter_value_assignmentInst: // IEEE: parameter_value_assignment for instance - '#' '(' cellparamListE ')' { $$ = $3; } + '#' '(' instParamListE ')' { $$ = $3; } // // Parentheses are optional around a single parameter // // IMPORTANT: Below hardcoded in tokenPipeScanParam | '#' yaINTNUM { $$ = new AstPin{$2, 1, "", new AstConst{$2, *$2}}; } @@ -1459,7 +1459,7 @@ parameter_value_assignmentInst: // IEEE: parameter_value_assignment parameter_value_assignmentClass: // IEEE: parameter_value_assignment (for classes) // // Like parameter_value_assignment, but for classes only, which always have #() - '#' '(' cellparamListE ')' { $$ = $3; } + '#' '(' instParamListE ')' { $$ = $3; } ; parameter_port_listE: // IEEE: parameter_port_list + empty == parameter_value_assignment @@ -3017,7 +3017,7 @@ loop_generate_construct: // ==IEEE: loop_generate_construct initp->unlinkFrBackWithNext(); // Detach 2nd from varp, make 1st init blkp->addStmtsp(varp); } - // Statements are under 'genforp' as cells under this + // Statements are under 'genforp' as instances under this // for loop won't get an extra layer of hierarchy tacked on blkp->genforp(new AstGenFor{$1, initp, $5, $7, lowerNoBegp}); $$ = blkp; @@ -3324,8 +3324,8 @@ etcInst: // IEEE: module_instantiation + gate_instantiati instDecl: // // Disambigurated from data_declaration based on - // // idCell which is found as IEEE requires a later '(' - idCell parameter_value_assignmentInstE + // // idInst which is found as IEEE requires a later '(' + idInst parameter_value_assignmentInstE /*mid*/ { INSTPREP($1, *$1, $2); } /*cont*/ instnameList ';' { $$ = $4; @@ -3358,12 +3358,12 @@ instnameList: ; instnameParen: - id instRangeListE '(' cellpinListE ')' + id instRangeListE '(' instPinListE ')' { $$ = GRAMMARP->createCell($1, *$1, $4, $2); } ; instnameParenUdpn: // IEEE: part of udp_instance when no name_of_instance - '(' cellpinListE ')' // When UDP has empty name, unpacked dimensions must not be used + '(' instPinListE ')' // When UDP has empty name, unpacked dimensions must not be used { $$ = GRAMMARP->createCell($1, "", $2, nullptr); } ; @@ -3384,31 +3384,31 @@ instRange: { $$ = new AstRange{$1, $2, $4}; } ; -cellparamListE: - { GRAMMARP->pinPush(); } cellparamItListE { $$ = $2; GRAMMARP->pinPop(CRELINE()); } +instParamListE: + { GRAMMARP->pinPush(); } instParamItListE { $$ = $2; GRAMMARP->pinPop(CRELINE()); } ; -cellpinListE: - { VARRESET_LIST(UNKNOWN); } cellpinItListE { $$ = $2; VARRESET_NONLIST(UNKNOWN); } +instPinListE: + { VARRESET_LIST(UNKNOWN); } instPinItListE { $$ = $2; VARRESET_NONLIST(UNKNOWN); } ; -cellparamItListE: // IEEE: list_of_parameter_value_assignments/list_of_parameter_assignments +instParamItListE: // IEEE: list_of_parameter_value_assignments/list_of_parameter_assignments // // Empty gets a node, to track class reference of #() /*empty*/ { $$ = new AstPin{CRELINE(), PINNUMINC(), "", nullptr}; } - | cellparamItList { $$ = $1; } + | instParamItList { $$ = $1; } ; -cellparamItList: // IEEE: list_of_parameter_value_assignments/list_of_parameter_assignments - cellparamItem { $$ = $1; } - | cellparamItList ',' cellparamItem { $$ = addNextNull($1, $3); } +instParamItList: // IEEE: list_of_parameter_value_assignments/list_of_parameter_assignments + instParamItem { $$ = $1; } + | instParamItList ',' instParamItem { $$ = addNextNull($1, $3); } ; -cellpinItListE: // IEEE: list_of_port_connections - cellpinItemE { $$ = $1; } - | cellpinItListE ',' cellpinItemE { $$ = addNextNull($1, $3); } +instPinItListE: // IEEE: list_of_port_connections + instPinItemE { $$ = $1; } + | instPinItListE ',' instPinItemE { $$ = addNextNull($1, $3); } ; -cellparamItem: // IEEE: named_parameter_assignment + empty +instParamItem: // IEEE: named_parameter_assignment + empty // // Note empty is not allowed in parameter lists yP_DOTSTAR { $$ = new AstPin{$1, PINNUMINC(), ".*", nullptr}; } | '.' idAny '(' ')' @@ -3446,7 +3446,7 @@ cellparamItem: // IEEE: named_parameter_assignment + empty //UNSUP $$ = new AstPin{FILELINE_OR_CRE($3), PINNUMINC(), "", $3}; } ; -cellpinItemE: // IEEE: named_port_connection + empty +instPinItemE: // IEEE: named_port_connection + empty // // Note empty can match either () or (,); V3LinkCells cleans up () /* empty: ',,' is legal */ { $$ = new AstPin{CRELINE(), PINNUMINC(), "", nullptr}; } | yP_DOTSTAR { $$ = new AstPin{$1, PINNUMINC(), ".*", nullptr}; } @@ -4719,12 +4719,12 @@ funcId: // IEEE: function_data_type_or_implicit + part o { $$ = $2; $$->fvarp($1); SYMP->pushNewUnderNodeOrCurrent($$, $2); } - | packageClassScopeE idCellType packed_dimensionListE fIdScoped + | packageClassScopeE idInstType packed_dimensionListE fIdScoped { AstRefDType* const refp = new AstRefDType{$2, *$2, $1, nullptr}; $$ = $4; $$->fvarp(GRAMMARP->createArray(refp, $3, true)); SYMP->pushNewUnderNodeOrCurrent($$, $4); } - | packageClassScopeE idCellType parameter_value_assignmentClass packed_dimensionListE fIdScoped + | packageClassScopeE idInstType parameter_value_assignmentClass packed_dimensionListE fIdScoped { AstRefDType* const refp = new AstRefDType{$2, *$2, $1, $3}; $$ = $5; $$->fvarp(GRAMMARP->createArray(refp, $4, true)); @@ -5901,18 +5901,24 @@ id: | idRandomize { $$ = $1; $$ = $1; } ; -idAny: // Any kind of identifier +idAny: // Any kind of identifier yaID__ETC { $$ = $1; $$ = $1; } - | yaID__aCELL { $$ = $1; $$ = $1; } + | yaID__aINST { $$ = $1; $$ = $1; } | yaID__aTYPE { $$ = $1; $$ = $1; } | idRandomize { $$ = $1; $$ = $1; } ; -idCell: // IEEE: instance_identifier or similar with another id then '(' - // // See V3ParseImp::tokenPipeScanIdCell +idAnyAsParseRef: // Any kind of identifier as a ParseRef + idAny + { $$ = new AstParseRef{$1, VParseRefExp::PX_TEXT, *$1}; } + ; + + +idInst: // IEEE: instance_identifier or similar with another id then '(' + // // See V3ParseImp::tokenPipeScanIdInst // // [^': '@' '.'] yaID/*module_id*/ [ '#' '('...')' ] yaID/*name_of_instance*/ [ '['...']' ] '(' ... // // [^':' @' '.'] yaID/*module_id*/ [ '#' id|etc ] yaID/*name_of_instance*/ [ '['...']' ] '(' ... - yaID__aCELL { $$ = $1; $$ = $1; } + yaID__aINST { $$ = $1; $$ = $1; } ; idType: // IEEE: class_identifier or other type identifier @@ -5920,8 +5926,8 @@ idType: // IEEE: class_identifier or other type identifi yaID__aTYPE { $$ = $1; $$ = $1; } ; -idCellType: // type_identifier for functions which have a following id then '(' - yaID__aCELL { $$ = $1; $$ = $1; } +idInstType: // type_identifier for functions which have a following id then '(' + yaID__aINST { $$ = $1; $$ = $1; } | yaID__aTYPE { $$ = $1; $$ = $1; } ; @@ -6171,8 +6177,8 @@ list_of_clocking_decl_assign: // IEEE: list_of_clocking_decl_assign ; clocking_decl_assign: // IEEE: clocking_decl_assign - idAny/*new-signal_identifier*/ exprEqE - { AstParseRef* const refp = new AstParseRef{$1, VParseRefExp::PX_TEXT, *$1, nullptr, nullptr}; + idAnyAsParseRef/*new-signal_identifier*/ exprEqE + { AstParseRef* const refp = $1; $$ = refp; if ($2) $$ = new AstAssign{$2, refp, $2}; } ; @@ -6196,8 +6202,8 @@ clocking_skew: // IEEE: clocking_skew cycle_delay: // IEEE: cycle_delay yP_POUNDPOUND yaINTNUM { $$ = new AstDelay{$1, new AstConst{$2, *$2}, true}; } - | yP_POUNDPOUND idAny - { $$ = new AstDelay{$1, new AstParseRef{$2, VParseRefExp::PX_TEXT, *$2, nullptr, nullptr}, true}; } + | yP_POUNDPOUND idAnyAsParseRef + { $$ = new AstDelay{$1, $2, true}; } | yP_POUNDPOUND '(' expr ')' { $$ = new AstDelay{$1, $3, true}; } ; @@ -7295,8 +7301,8 @@ checker_generate_item: // ==IEEE: checker_generate_item //UNSUPchecker_instantiation: //UNSUP // // Only used for procedural_assertion_item's //UNSUP // // Version in concurrent_assertion_item looks like etcInst -//UNSUP // // Thus instead of *_checker_port_connection we can use etcInst's cellpinListE -//UNSUP id/*checker_identifier*/ id '(' cellpinListE ')' ';' { } +//UNSUP // // Thus instead of *_checker_port_connection we can use etcInst's instPinListE +//UNSUP id/*checker_identifier*/ id '(' instPinListE ')' ';' { } //UNSUP ; //********************************************************************** diff --git a/test_regress/t/t_config_libmap.map b/test_regress/t/t_config_libmap.map index fe1f6e3f2..e2d7e6e46 100644 --- a/test_regress/t/t_config_libmap.map +++ b/test_regress/t/t_config_libmap.map @@ -1,3 +1,4 @@ +// -*- Verilog -*- // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for @@ -12,6 +13,10 @@ library rtllib2 *.v, *.sv; library rtllib3 *.v -incdir *.vh; library rtllib4 *.v -incdir *.vh, *.svh; +// Note this does not start a comment +library gatelib ./*.vg; +// */ + config cfg; design t; endconfig diff --git a/test_regress/t/t_config_libmap.out b/test_regress/t/t_config_libmap.out index d86495d89..0415f7ac2 100644 --- a/test_regress/t/t_config_libmap.out +++ b/test_regress/t/t_config_libmap.out @@ -1,29 +1,32 @@ -%Error-UNSUPPORTED: t/t_config_libmap.map:8:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' +%Error-UNSUPPORTED: t/t_config_libmap.map:9:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'include' : ... Suggest unless in a lib.map file, want `include instead - 8 | include ./t_config_libmap_inc.map + 9 | include ./t_config_libmap_inc.map | ^~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_config_libmap.map:8:9: syntax error, unexpected '.' - 8 | include ./t_config_libmap_inc.map +%Error: t/t_config_libmap.map:9:9: syntax error, unexpected '.' + 9 | include ./t_config_libmap_inc.map | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_config_libmap.map:10:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' - 10 | library rtllib *.v; - | ^~~~~~~~~~~~~~ %Error-UNSUPPORTED: t/t_config_libmap.map:11:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' - 11 | library rtllib2 *.v, *.sv; + 11 | library rtllib *.v; | ^~~~~~~~~~~~~~ %Error-UNSUPPORTED: t/t_config_libmap.map:12:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' - 12 | library rtllib3 *.v -incdir *.vh; + 12 | library rtllib2 *.v, *.sv; | ^~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:12:29: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'incdir' %Error-UNSUPPORTED: t/t_config_libmap.map:13:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' - 13 | library rtllib4 *.v -incdir *.vh, *.svh; + 13 | library rtllib3 *.v -incdir *.vh; | ^~~~~~~~~~~~~~ %Error-UNSUPPORTED: t/t_config_libmap.map:13:29: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'incdir' - 13 | library rtllib4 *.v -incdir *.vh, *.svh; +%Error-UNSUPPORTED: t/t_config_libmap.map:14:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 14 | library rtllib4 *.v -incdir *.vh, *.svh; + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:14:29: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'incdir' + 14 | library rtllib4 *.v -incdir *.vh, *.svh; | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_config_libmap.map:15:1: Unsupported: Verilog 2001-config reserved word not implemented: 'config' -%Error-UNSUPPORTED: t/t_config_libmap.map:16:4: Unsupported: Verilog 2001-config reserved word not implemented: 'design' -%Error-UNSUPPORTED: t/t_config_libmap.map:17:1: Unsupported: Verilog 2001-config reserved word not implemented: 'endconfig' +%Error-UNSUPPORTED: t/t_config_libmap.map:17:1: Unsupported: Verilog 2001-config lib.map reserved word not implemented: 'library' + 17 | library gatelib . + | ^~~~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_config_libmap.map:20:1: Unsupported: Verilog 2001-config reserved word not implemented: 'config' +%Error-UNSUPPORTED: t/t_config_libmap.map:21:4: Unsupported: Verilog 2001-config reserved word not implemented: 'design' +%Error-UNSUPPORTED: t/t_config_libmap.map:22:1: Unsupported: Verilog 2001-config reserved word not implemented: 'endconfig' %Error: Exiting due to diff --git a/test_regress/t/t_config_libmap_inc.map b/test_regress/t/t_config_libmap_inc.map index fe1f6e3f2..44a8994ce 100644 --- a/test_regress/t/t_config_libmap_inc.map +++ b/test_regress/t/t_config_libmap_inc.map @@ -1,3 +1,4 @@ +// -*- Verilog -*- // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain, for diff --git a/test_regress/t/t_param_type_bad.out b/test_regress/t/t_param_type_bad.out index f7f374e6f..a9bf7634b 100644 --- a/test_regress/t/t_param_type_bad.out +++ b/test_regress/t/t_param_type_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_param_type_bad.v:9:27: syntax error, unexpected INTEGER NUMBER, expecting IDENTIFIER or IDENTIFIER-for-cell or IDENTIFIER-for-type or randomize +%Error: t/t_param_type_bad.v:9:27: syntax error, unexpected INTEGER NUMBER, expecting IDENTIFIER or IDENTIFIER-for-instance or IDENTIFIER-for-type or randomize 9 | localparam type bad2 = 2; | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. From d47b88a30c0c8f8b6d5b7cddf2bb980c4e206683 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 15:48:23 -0400 Subject: [PATCH 028/211] Internals: Remove accidental debug message --- src/V3Undriven.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 7e9903018..c9ae78929 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -469,8 +469,6 @@ class UndrivenVisitor final : public VNVisitorConst { entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline()); } if (nodep->access().isWriteOrRW()) { - UINFO(1, "ww is=" << m_inInitialStatic << " ipa=" << m_inProcAssign << " " << nodep - << endl); if (m_inInitialStatic && !entryp->initStaticp()) entryp->initStaticp(nodep); if (m_alwaysp && m_inProcAssign && !entryp->procWritep()) entryp->procWritep(nodep); From 27ad648c1654060e799ccaad734e14d8306c4373 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 16:24:36 -0400 Subject: [PATCH 029/211] Commentary: Indicate V3Number width() criticality. --- src/V3Ast.cpp | 2 +- src/V3Clock.cpp | 2 +- src/V3Const.cpp | 22 +++++++++++----------- src/V3LinkResolve.cpp | 6 +++--- src/V3Number.cpp | 22 ++++++++++++++++++++++ src/V3Number.h | 16 +++++++++++++--- 6 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 2fa02f3ff..57d2f5434 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -324,7 +324,7 @@ void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int line // // Commenting out the section below may crash, as the tree state // // between edits is not always consistent for printing // cout<<"-treeChange: V3Ast.cpp:"<dumpTree("- treeChange: "); +// if (debug()) v3Global.rootp()->dumpTree("- treeChange: "); // if (next||1) nodep->dumpTreeAndNext(cout, prefix); // else nodep->dumpTree(prefix); // nodep->checkTree(); diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index ec6d8bf21..0fbdb9a27 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor { } // VISITORS void visit(AstCoverToggle* nodep) override { - // nodep->dumpTree("- ct: "); + // if (debug()) nodep->dumpTree("- ct: "); // COVERTOGGLE(INC, ORIG, CHANGE) -> // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } AstNode* const incp = nodep->incp()->unlinkFrBack(); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index c2ef9e0d5..ccaf1d02b 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1706,7 +1706,7 @@ class ConstVisitor final : public VNVisitor { void replaceAsv(AstNodeBiop* nodep) { // BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c) // BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c) - // nodep->dumpTree("- repAsvConst_old: "); + // if (debug()) nodep->dumpTree("- repAsvConst_old: "); AstNodeExpr* const ap = nodep->lhsp(); AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop); AstNodeExpr* const bp = rp->lhsp(); @@ -1720,7 +1720,7 @@ class ConstVisitor final : public VNVisitor { rp->lhsp(ap); rp->rhsp(bp); if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp); - // nodep->dumpTree("- repAsvConst_new: "); + // if (debug()) nodep->dumpTree("- repAsvConst_new: "); } void replaceAsvLUp(AstNodeBiop* nodep) { // BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) @@ -1732,7 +1732,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(lp); lp->lhsp(lrp); lp->rhsp(rp); - // nodep->dumpTree("- repAsvLUp_new: "); + // if (debug()) nodep->dumpTree("- repAsvLUp_new: "); } void replaceAsvRUp(AstNodeBiop* nodep) { // BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) @@ -1744,7 +1744,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(rp); rp->lhsp(lp); rp->rhsp(rrp); - // nodep->dumpTree("- repAsvRUp_new: "); + // if (debug()) nodep->dumpTree("- repAsvRUp_new: "); } void replaceAndOr(AstNodeBiop* nodep) { // OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr)) @@ -1777,7 +1777,7 @@ class ConstVisitor final : public VNVisitor { } else { nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched"); } - // nodep->dumpTree("- repAndOr_new: "); + // if (debug()) nodep->dumpTree("- repAndOr_new: "); } void replaceShiftSame(AstNodeBiop* nodep) { // Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr) @@ -1796,7 +1796,7 @@ class ConstVisitor final : public VNVisitor { nodep->dtypep(llp->dtypep()); // dtype of Biop is before shift. VL_DO_DANGLING(pushDeletep(rp), rp); VL_DO_DANGLING(pushDeletep(rrp), rrp); - // nodep->dumpTree("- repShiftSame_new: "); + // if (debug()) nodep->dumpTree("- repShiftSame_new: "); } void replaceConcatSel(AstConcat* nodep) { // {a[1], a[0]} -> a[1:0] @@ -1981,7 +1981,7 @@ class ConstVisitor final : public VNVisitor { newp->dtypeFrom(nodep); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); - // newp->dumpTree("- repShiftShift_new: "); + // if (debug()) newp->dumpTree("- repShiftShift_new: "); iterate(newp); // Further reduce, either node may have more reductions. } VL_DO_DANGLING(pushDeletep(lhsp), lhsp); @@ -2024,8 +2024,8 @@ class ConstVisitor final : public VNVisitor { const bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt()); UINFO(4, "replaceAssignMultiSel " << nodep << endl); UINFO(4, " && " << nextp << endl); - // nodep->dumpTree("- comb1: "); - // nextp->dumpTree("- comb2: "); + // if (debug()) nodep->dumpTree("- comb1: "); + // if (debug()) nextp->dumpTree("- comb2: "); AstNodeExpr* const rhs1p = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const rhs2p = nextp->rhsp()->unlinkFrBack(); AstNodeAssign* newp; @@ -2038,7 +2038,7 @@ class ConstVisitor final : public VNVisitor { sel2p->lsbConst(), sel1p->width() + sel2p->width()}, new AstConcat{rhs1p->fileline(), rhs1p, rhs2p}); } - // pnewp->dumpTree("- conew: "); + // if (debug()) pnewp->dumpTree("- conew: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBack()), nextp); @@ -2950,7 +2950,7 @@ class ConstVisitor final : public VNVisitor { void visit(AstSenTree* nodep) override { iterateChildren(nodep); if (m_doExpensive) { - // cout<dumpTree("- ssin: "); + // if (debug()) nodep->dumpTree("- ssin: "); // Optimize ideas for the future: // SENTREE(... SENGATE(x,a), SENGATE(SENITEM(x),b) ...) => SENGATE(x,OR(a,b)) diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index cf3572720..d1c96f131 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -190,8 +190,8 @@ class LinkResolveVisitor final : public VNVisitor { if (VN_IS(nodep->backp(), StmtExpr)) { nodep->v3error("Expected statement, not let substitution " << letp->prettyNameQ()); } - // letp->dumpTree("-let-let "); - // nodep->dumpTree("-let-ref "); + // if (debug()) letp->dumpTree("-let-let "); + // if (debug()) nodep->dumpTree("-let-ref "); AstStmtExpr* const letStmtp = VN_AS(letp->stmtsp(), StmtExpr); AstNodeExpr* const newp = letStmtp->exprp()->cloneTree(false); const V3TaskConnects tconnects = V3Task::taskConnects(nodep, letp->stmtsp()); @@ -214,7 +214,7 @@ class LinkResolveVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(refp), refp); } }); - // newp->dumpTree("-let-new "); + // if (debug()) newp->dumpTree("-let-new "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); // Iterate to expand further now, so we can look for recursions diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 3161a5b52..9d9d3a2e1 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -532,6 +532,7 @@ V3Number& V3Number::setMask(int nbits, int lsb) { // ACCESSORS - as strings string V3Number::ascii(bool prefixed, bool cleanVerilog) const VL_MT_STABLE { + // Correct number of zero bits/width matters std::ostringstream out; if (is1Step()) { @@ -643,6 +644,7 @@ string V3Number::displayed(AstNode* nodep, const string& vformat) const VL_MT_ST } string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STABLE { + // Correct number of zero bits/width matters auto pos = vformat.cbegin(); UASSERT(pos != vformat.cend() && pos[0] == '%', "$display-like function with non format argument " << *this); @@ -882,6 +884,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB } string V3Number::toDecimalS() const VL_MT_STABLE { + // Correct number of zero bits/width matters if (isNegative()) { V3Number lhsNoSign = *this; lhsNoSign.opNegate(*this); @@ -1082,6 +1085,7 @@ bool V3Number::isEqOne() const { return true; } bool V3Number::isEqAllOnes(int optwidth) const { + // Correct number of zero bits/width matters if (!optwidth) optwidth = width(); for (int bit = 0; bit < optwidth; bit++) { if (!bitIs1(bit)) return false; @@ -1145,6 +1149,7 @@ int V3Number::widthToFit() const { } uint32_t V3Number::countBits(const V3Number& ctrl) const { + // Correct number of zero bits/width matters int n = 0; for (int bit = 0; bit < width(); ++bit) { switch (ctrl.bitIs(0)) { @@ -1167,6 +1172,7 @@ uint32_t V3Number::countBits(const V3Number& ctrl) const { uint32_t V3Number::countBits(const V3Number& ctrl1, const V3Number& ctrl2, const V3Number& ctrl3) const { + // Correct number of zero bits/width matters int n = countBits(ctrl1); if (ctrl2.bitIs(0) != ctrl1.bitIs(0)) n += countBits(ctrl2); if ((ctrl3.bitIs(0) != ctrl1.bitIs(0)) && (ctrl3.bitIs(0) != ctrl2.bitIs(0))) { @@ -1192,6 +1198,7 @@ uint32_t V3Number::mostSetBitP1() const { //====================================================================== V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0 + // Correct number of zero bits/width matters // op i, L(lhs) bit return NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); @@ -1252,6 +1259,7 @@ V3Number& V3Number::opRedOr(const V3Number& lhs) { } V3Number& V3Number::opRedAnd(const V3Number& lhs) { + // Correct number of zero bits/width matters // op i, 1 bit return NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); @@ -1289,6 +1297,7 @@ V3Number& V3Number::opRedXor(const V3Number& lhs) { V3Number& V3Number::opCountBits(const V3Number& expr, const V3Number& ctrl1, const V3Number& ctrl2, const V3Number& ctrl3) { + // Correct number of zero bits/width matters NUM_ASSERT_OP_ARGS4(expr, ctrl1, ctrl2, ctrl3); NUM_ASSERT_LOGIC_ARGS4(expr, ctrl1, ctrl2, ctrl3); setZero(); @@ -1354,6 +1363,7 @@ last: } V3Number& V3Number::opNot(const V3Number& lhs) { + // Correct number of zero bits/width matters NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); // op i, L(lhs) bit return @@ -1420,6 +1430,7 @@ V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) { } V3Number& V3Number::opConcat(const V3Number& lhs, const V3Number& rhs) { + // Correct number of zero bits/width matters NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); setZero(); @@ -1834,6 +1845,7 @@ V3Number& V3Number::opShiftR(const V3Number& lhs, const V3Number& rhs) { } V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t lbits) { + // Correct number of zero bits/width matters (hance lbits passed) // L(lhs) bit return // The spec says a unsigned >>> still acts as a normal >>. // We presume it is signed; as that's V3Width's job to convert to opShiftR @@ -1881,6 +1893,7 @@ V3Number& V3Number::opShiftL(const V3Number& lhs, const V3Number& rhs) { // Ops - Arithmetic V3Number& V3Number::opNegate(const V3Number& lhs) { + // Correct number of zero bits/width matters // op i, L(lhs) bit return NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); @@ -1910,6 +1923,7 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) { return *this; } V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) { + // Correct number of zero bits/width matters // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); @@ -1949,6 +1963,7 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) { } V3Number& V3Number::opMulS(const V3Number& lhs, const V3Number& rhs) { // Signed multiply + // Correct number of zero bits/width matters NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); @@ -1981,6 +1996,7 @@ V3Number& V3Number::opDiv(const V3Number& lhs, const V3Number& rhs) { } V3Number& V3Number::opDivS(const V3Number& lhs, const V3Number& rhs) { // Signed divide + // Correct number of zero bits/width matters // UINFO(9, ">>divs-start "<= m_data.width()) return bitIs1Extend(m_data.width() - 1); @@ -457,7 +458,10 @@ private: int countZ(int lsb, int nbits) const VL_MT_SAFE; int words() const VL_MT_SAFE { return ((width() + 31) / 32); } - uint32_t hiWordMask() const VL_MT_SAFE { return VL_MASK_I(width()); } + uint32_t hiWordMask() const VL_MT_SAFE { + // Correct number of zero bits/width matters + return VL_MASK_I(width()); + } V3Number& opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool is_modulus); @@ -615,7 +619,10 @@ public: return m_data.type() == V3NumberDataType::LOGIC || m_data.type() == V3NumberDataType::DOUBLE; } - bool isNegative() const VL_MT_SAFE { return !isString() && bitIs1(width() - 1); } + bool isNegative() const VL_MT_SAFE { + // Correct number of zero bits/width matters + return !isString() && bitIs1(width() - 1); + } bool is1Step() const VL_MT_SAFE { return m_data.m_is1Step; } bool isNull() const VL_MT_SAFE { return m_data.m_isNull; } bool isFourState() const VL_MT_SAFE; @@ -639,7 +646,10 @@ public: bool isAnyX() const VL_MT_SAFE; bool isAnyXZ() const; bool isAnyZ() const VL_MT_SAFE; - bool isMsbXZ() const { return bitIsXZ(m_data.width() - 1); } + bool isMsbXZ() const { + // Correct number of zero bits/width matters + return bitIsXZ(width() - 1); + } bool fitsInUInt() const VL_MT_SAFE; uint32_t toUInt() const VL_MT_SAFE; int32_t toSInt() const VL_MT_SAFE; From 11cfa61f80a0ca0cc5245984a86a92dace8abe37 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 16:34:37 -0400 Subject: [PATCH 030/211] Fix casting etc of typedef'ed doubles. --- src/V3AstInlines.h | 2 +- src/V3Number.h | 2 +- test_regress/t/t_class_param.v | 11 +++++++---- test_regress/t/t_cover_toggle.out | 2 +- test_regress/t/t_cover_toggle__points.out | 4 +--- test_regress/t/t_dpi_open_oob_bad.out | 24 +++++++++++++++-------- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index cb13fa21a..3542839b1 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -34,7 +34,7 @@ int AstNode::widthInstrs() const { return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); } bool AstNode::isDouble() const VL_MT_STABLE { - return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble(); + return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isDouble(); } bool AstNode::isString() const VL_MT_STABLE { return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); diff --git a/src/V3Number.h b/src/V3Number.h index 5b6fa517e..85cd4d150 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -408,7 +408,7 @@ public: if (bit < 0) return false; if (bit >= m_data.width()) return !bitIsXZ(m_data.width() - 1); const ValueAndX v = m_data.num()[bit / 32]; - return ((v.m_value & (1UL << (bit & 31))) == 0 && !(v.m_valueX & (1UL << (bit & 31)))); + return ((v.m_value | v.m_valueX) & (1UL << (bit & 31))) == 0; } bool bitIs1(int bit) const VL_MT_SAFE { if (!isNumber()) return false; diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index 08dd1f61b..38757c1a9 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -62,7 +62,7 @@ class Sum #(type T); static int sum; static function void add(T element); sum += int'(element); - endfunction + endfunction endclass class IntQueue; @@ -221,8 +221,11 @@ module t (/*AUTOARG*/); qi.q = '{2, 4, 6, 0, 2}; if (qi.getSum() != 14) $stop; Sum#(int)::add(arr[0]); - if(Sum#(int)::sum != 16) $stop; - if(Sum#(real)::sum != 0) $stop; + if (Sum#(int)::sum != 16) $stop; + + if (Sum#(real)::sum != 0) $stop; + Sum#(real)::add(1.9); // rounds + if (Sum#(real)::sum != 2) $stop; if (ClsParam#(ClsStatic)::param_t::x != 1) $stop; if (ClsParam#(ClsStatic)::param_t::get_2() != 2) $stop; @@ -231,7 +234,7 @@ module t (/*AUTOARG*/); if (cls_param_field.get(2) != 7) $stop; dict_op.set("abcd", 1); - if(dict_op.get("abcd") != 1) $stop; + if (dict_op.get("abcd") != 1) $stop; if (getter1.get_1() != 1) $stop; if (Getter1#()::get_1() != 1) $stop; diff --git a/test_regress/t/t_cover_toggle.out b/test_regress/t/t_cover_toggle.out index 065e014a0..5d0010643 100644 --- a/test_regress/t/t_cover_toggle.out +++ b/test_regress/t/t_cover_toggle.out @@ -12,7 +12,7 @@ 000019 input clk; input real check_real; // Check issue #2741 - 000021 input real check_array_real [1:0]; + input real check_array_real [1:0]; input string check_string; // Check issue #2766 typedef struct packed { diff --git a/test_regress/t/t_cover_toggle__points.out b/test_regress/t/t_cover_toggle__points.out index 17279cd14..7e220867b 100644 --- a/test_regress/t/t_cover_toggle__points.out +++ b/test_regress/t/t_cover_toggle__points.out @@ -13,9 +13,7 @@ 000019 input clk; +000019 point: comment=clk hier=top.t input real check_real; // Check issue #2741 - 000021 input real check_array_real [1:0]; -+000021 point: comment=check_array_real[0] hier=top.t -+000021 point: comment=check_array_real[1] hier=top.t + input real check_array_real [1:0]; input string check_string; // Check issue #2766 typedef struct packed { diff --git a/test_regress/t/t_dpi_open_oob_bad.out b/test_regress/t/t_dpi_open_oob_bad.out index a40343874..b89413bf3 100644 --- a/test_regress/t/t_dpi_open_oob_bad.out +++ b/test_regress/t/t_dpi_open_oob_bad.out @@ -5,14 +5,22 @@ dpii_int_u3: %Warning: DPI svOpenArrayHandle function index 1 out of bounds; 10 outside [2:-2]. %Warning: DPI svOpenArrayHandle function called on 3 dimensional array using 1 dimensional function. dpii_real_u1: -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). -%Warning: DPI svOpenArrayHandle function unsupported datatype (5). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). +%Warning: DPI svOpenArrayHandle function unsupported datatype (8). dpii_bit_u6: %Warning: DPI svOpenArrayHandle function called on 6 dimensional array using -1 dimensional function. %Warning: DPI svOpenArrayHandle function called on 6 dimensional array using -1 dimensional function. From 413183bad88fdfe8e6a9cdd4984113d82d34ebd0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 21:25:44 -0400 Subject: [PATCH 031/211] Fix localize of coroutines (#5972 partial) --- src/V3Localize.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 5a1014a62..43f890110 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -132,6 +132,11 @@ class LocalizeVisitor final : public VNVisitor { moveVarScopes(); } + void visit(AstCAwait* nodep) override { + m_cfuncp->user1(true); // Mark caller as not a leaf function + iterateChildrenConst(nodep); + } + void visit(AstCFunc* nodep) override { UINFO(4, " CFUNC " << nodep << endl); VL_RESTORER(m_cfuncp); From 66e105b444c894c9621ab37bd584c270c608b8ef Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 4 May 2025 21:41:14 -0400 Subject: [PATCH 032/211] Fix constant propagation of post-expand stages (#5963) (#5972). --- Changes | 2 +- src/V3AstNodeDType.h | 2 + src/V3Const.cpp | 44 +++++++++++------ src/V3Number.h | 1 + test_regress/t/t_math_cv_concat.py | 18 +++++++ test_regress/t/t_math_cv_concat.v | 42 ++++++++++++++++ test_regress/t/t_math_cv_format.py | 18 +++++++ test_regress/t/t_math_cv_format.v | 78 ++++++++++++++++++++++++++++++ 8 files changed, 190 insertions(+), 15 deletions(-) create mode 100755 test_regress/t/t_math_cv_concat.py create mode 100644 test_regress/t/t_math_cv_concat.v create mode 100755 test_regress/t/t_math_cv_format.py create mode 100644 test_regress/t/t_math_cv_format.v diff --git a/Changes b/Changes index 49b7e80b6..e4a4d1a5a 100644 --- a/Changes +++ b/Changes @@ -17,7 +17,7 @@ Verilator 5.037 devel * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). * Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore] -* Fix constant propagation making upper bits Xs (#5955) (#5969). +* Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] * Fix streaming to/from packed arrays (#5976). [Geza Lore] diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index bbcb8fcc3..81c93c04c 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -427,6 +427,8 @@ public: string prettyDTypeName(bool full) const override; const char* broken() const override { BROKEN_RTN(dtypep() != this); + BROKEN_RTN(v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH + && widthMin() > width()); return nullptr; } void setSignedState(const VSigning& signst) { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ccaf1d02b..dd4eadbf6 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -927,6 +927,18 @@ class ConstVisitor final : public VNVisitor { // METHODS + V3Number constNumV(AstNode* nodep) { + // Contract C width to V width (if needed, else just direct copy) + // The upper zeros in the C representation can otherwise cause + // wrong results in some operations, e.g. MulS + const V3Number& numc = VN_AS(nodep, Const)->num(); + return !numc.isNumber() ? numc : V3Number{nodep, nodep->widthMinV(), numc}; + } + V3Number toNumC(AstNode* nodep, V3Number& numv) { + // Extend V width back to C width for given node + return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv}; + } + bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); } bool operandAsvConst(const AstNode* nodep) { // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) @@ -1614,31 +1626,32 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(replaceNum(nodep, ones), nodep); } void replaceConst(AstNodeUniop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "UNICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeBiop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(), - VN_AS(nodep->rhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "BICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeTriop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(), - VN_AS(nodep->rhsp(), Const)->num(), - VN_AS(nodep->thsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()), + constNumV(nodep->thsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "TRICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeQuadop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate( - num, VN_AS(nodep->lhsp(), Const)->num(), VN_AS(nodep->rhsp(), Const)->num(), - VN_AS(nodep->thsp(), Const)->num(), VN_AS(nodep->fhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()), + constNumV(nodep->thsp()), constNumV(nodep->fhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "QUADCONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } @@ -1719,6 +1732,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(cp); rp->lhsp(ap); rp->rhsp(bp); + rp->dtypeFrom(nodep); // Upper widthMin more likely correct if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp); // if (debug()) nodep->dumpTree("- repAsvConst_new: "); } @@ -1732,6 +1746,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(lp); lp->lhsp(lrp); lp->rhsp(rp); + lp->dtypeFrom(nodep); // Upper widthMin more likely correct // if (debug()) nodep->dumpTree("- repAsvLUp_new: "); } void replaceAsvRUp(AstNodeBiop* nodep) { @@ -1744,6 +1759,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(rp); rp->lhsp(lp); rp->rhsp(rrp); + rp->dtypeFrom(nodep); // Upper widthMin more likely correct // if (debug()) nodep->dumpTree("- repAsvRUp_new: "); } void replaceAndOr(AstNodeBiop* nodep) { @@ -3278,7 +3294,7 @@ class ConstVisitor final : public VNVisitor { if (argp) { AstNode* const nextp = argp->nextp(); if (VN_IS(argp, Const)) { // Convert it - const string out = VN_AS(argp, Const)->num().displayed(nodep, fmt); + const string out = constNumV(argp).displayed(nodep, fmt); UINFO(9, " DispConst: " << fmt << " -> " << out << " for " << argp << endl); // fmt = out w/ replace % with %% as it must be literal. diff --git a/src/V3Number.h b/src/V3Number.h index 85cd4d150..d092e8f96 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -639,6 +639,7 @@ public: bool isEqZero() const VL_MT_SAFE; bool isNeqZero() const; bool isBitsZero(int msb, int lsb) const; + bool isBroken(int vwidth) const; bool isEqOne() const; bool isEqAllOnes(int optwidth = 0) const; bool isCaseEq(const V3Number& rhs) const; // operator== diff --git a/test_regress/t/t_math_cv_concat.py b/test_regress/t/t_math_cv_concat.py new file mode 100755 index 000000000..4ff66dda6 --- /dev/null +++ b/test_regress/t/t_math_cv_concat.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', '-fno-expand']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_cv_concat.v b/test_regress/t/t_math_cv_concat.v new file mode 100644 index 000000000..5f4b9e37d --- /dev/null +++ b/test_regress/t/t_math_cv_concat.v @@ -0,0 +1,42 @@ +// 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 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0h\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t; + // Issue #5972 + + reg clk; + reg signed [28:28] in1; + reg signed [21:8] reg_10; + + // verilator lint_off WIDTHEXPAND + always @(negedge clk) begin + // Issue #5972 + reg_10[14:8] <= {1'b1, ~((in1[28:28] & ~(in1[28:28])))}; + end + + initial begin + clk = 1; + in1 = 1'b0; + reg_10 = '0; + #2; + clk = 0; + #2; + `checkh(reg_10, 3); + + in1 = 1'b1; + clk = 1; + #2; + clk = 0; + #2; + `checkh(reg_10, 3); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_math_cv_format.py b/test_regress/t/t_math_cv_format.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_math_cv_format.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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_cv_format.v b/test_regress/t/t_math_cv_format.v new file mode 100644 index 000000000..1896c2bc3 --- /dev/null +++ b/test_regress/t/t_math_cv_format.v @@ -0,0 +1,78 @@ +// 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 + +`define stop $stop +`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t; + wire signed [21:10] out0; + + sub sub ( + .out0(out0) + ); + + sub2 sub2 (); + + string s; + + initial begin + #20; + // Bug with sformat, so can't just number-compare + s = $sformatf("out0=%0d", out0); + `checks(s, "out0=-12"); + if (out0 > 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module sub (out0); + reg signed [27:20] reg_4; + output wire [21:10] out0; + + initial begin + #1; + reg_4 = 0; + end + + wire [11:0] w55; + wire [11:0] w23; + // verilator lint_off WIDTHEXPAND + assign w55 = ~reg_4[20]; + // verilator lint_on WIDTHEXPAND + assign { w23[3], w23[1:0] } = 3'h0; + assign { w23[11:4], w23[2] } = { w55[11:4], w55[2] }; + assign out0 = w23; +endmodule + +module sub2; + reg [27:5] in0; + reg [26:11] in1; + wire [24:14] wire_0; + wire [26:5] out1; + wire w085; + wire w082; + wire [10:0] w092; + wire [9:0] w028; + + string s; + + initial begin + in0 = 6902127; + in1 = 10000; + #10; + s = $sformatf("out0=%0d", out1); + `checks(s, "out0=0"); + end + + assign w028 = ~ { 9'h000, in0[23] }; + assign w092[1] = 1'h0; + assign { w092[10:2], w092[0] } = w028; + assign wire_0 = w092; + assign w082 = | wire_0[18:17]; + assign w085 = w082 ? in1[11] : 1'h0; + assign out1 = { 21'h000000, w085 }; +endmodule From 1e74451534fa6c6b657360a40c7005b379f9d2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Mon, 5 May 2025 11:25:39 +0200 Subject: [PATCH 033/211] Internals: Change naming convention for Vthread funcs (#5982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bartłomiej Chmiel --- src/V3ExecGraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index f3e69a89a..4052f7836 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -771,8 +771,8 @@ const std::vector createThreadFunctions(const ThreadSchedule& schedul for (const std::vector& thread : schedule.threads) { if (thread.empty()) continue; const uint32_t threadId = schedule.threadId(thread.front()); - const string name{"__Vthread__" + tag + "__t" + cvtToStr(threadId) + "__s" - + cvtToStr(schedule.id())}; + const string name{"__Vthread__" + tag + "__s" + cvtToStr(schedule.id()) + "__t" + + cvtToStr(threadId)}; AstCFunc* const funcp = new AstCFunc{fl, name, nullptr, "void"}; modp->addStmtsp(funcp); funcps.push_back(funcp); From a3662cc3f584de1950a284c1fed3292caf51417c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 5 May 2025 06:31:06 -0400 Subject: [PATCH 034/211] Internals: Refactor to create replaceWithKeepDType. No functional change. --- src/V3Ast.cpp | 4 +++ src/V3Ast.h | 1 + src/V3Const.cpp | 63 +++++++++++++++------------------------------ src/V3Premit.cpp | 3 +-- src/V3Width.cpp | 12 +++------ src/V3WidthRemove.h | 3 +-- 6 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 57d2f5434..c630c96fc 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -521,6 +521,10 @@ void AstNode::replaceWith(AstNode* newp) { this->unlinkFrBack(&repHandle); repHandle.relink(newp); } +void AstNode::replaceWithKeepDType(AstNode* newp) { + newp->dtypeFrom(this); + replaceWith(newp); +} void VNRelinker::dump(std::ostream& str) const { str << " BK=" << reinterpret_cast(m_backp); diff --git a/src/V3Ast.h b/src/V3Ast.h index 1795b683b..37ec30133 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2415,6 +2415,7 @@ public: void addNextHere(AstNode* newp); // Insert newp at this->nextp void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next void replaceWith(AstNode* newp); // Replace current node in tree with new node + void replaceWithKeepDType(AstNode* newp); // Replace current node in tree, keep old dtype // Unlink this from whoever points to it. AstNode* unlinkFrBack(VNRelinker* linkerp = nullptr); // Unlink this from whoever points to it, keep entire next list with unlinked node diff --git a/src/V3Const.cpp b/src/V3Const.cpp index dd4eadbf6..864c3a283 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1096,10 +1096,9 @@ class ConstVisitor final : public VNVisitor { new AstAnd{nodep->fileline(), maskp->cloneTree(false), condp->thenp()->unlinkFrBack()}, new AstAnd{nodep->fileline(), maskp->cloneTree(false), condp->elsep()->unlinkFrBack()})); - newp->dtypeFrom(nodep); newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths newp->elsep()->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -1155,8 +1154,7 @@ class ConstVisitor final : public VNVisitor { if (constp->num().isCaseEq(mask)) { AstNode* const rhsp = nodep->rhsp(); rhsp->unlinkFrBack(); - nodep->replaceWith(rhsp); - rhsp->dtypeFrom(nodep); + nodep->replaceWithKeepDType(rhsp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -1354,9 +1352,8 @@ class ConstVisitor final : public VNVisitor { if (debug() >= 9) nodep->dumpTree("- SEL(SH)-in: "); AstSel* const newp = new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()}; - newp->dtypeFrom(nodep); + nodep->replaceWithKeepDType(newp); if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: "); - nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -1582,10 +1579,9 @@ class ConstVisitor final : public VNVisitor { UASSERT_OBJ(!(VN_IS(oldp, Const) && !VN_AS(oldp, Const)->num().isFourState()), oldp, "Already constant??"); AstNode* const newp = new AstConst{oldp->fileline(), num}; - newp->dtypeFrom(oldp); + oldp->replaceWithKeepDType(newp); if (debug() > 5) oldp->dumpTree("- const_old: "); if (debug() > 5) newp->dumpTree("- _new: "); - oldp->replaceWith(newp); VL_DO_DANGLING(pushDeletep(oldp), oldp); } void replaceNum(AstNode* nodep, uint32_t val) { @@ -1615,8 +1611,7 @@ class ConstVisitor final : public VNVisitor { } else { AstNode* const newp = new AstAnd{nodep->fileline(), new AstConst{nodep->fileline(), 0}, checkp->unlinkFrBack()}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } } @@ -1674,8 +1669,7 @@ class ConstVisitor final : public VNVisitor { childp->unlinkFrBackWithNext(); // If replacing a SEL for example, the data type comes from the parent (is less wide). // This may adversely affect the operation of the node being replaced. - childp->dtypeFrom(nodep); - nodep->replaceWith(childp); + nodep->replaceWithKeepDType(childp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceWChildBool(AstNode* nodep, AstNodeExpr* childp) { @@ -1872,8 +1866,7 @@ class ConstVisitor final : public VNVisitor { = (VN_IS(nodep, ExtendS) ? static_cast(new AstExtendS{nodep->fileline(), arg0p}) : static_cast(new AstExtend{nodep->fileline(), arg0p})); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replacePowShift(AstNodeBiop* nodep) { // Pow or PowS @@ -1881,9 +1874,8 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack(); AstShiftL* const newp = new AstShiftL{nodep->fileline(), new AstConst{nodep->fileline(), 1}, rhsp}; - newp->dtypeFrom(nodep); newp->lhsp()->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift @@ -1892,8 +1884,7 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const opp = nodep->rhsp()->unlinkFrBack(); AstShiftL* const newp = new AstShiftL{nodep->fileline(), opp, new AstConst(nodep->fileline(), amount)}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift @@ -1902,8 +1893,7 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const opp = nodep->lhsp()->unlinkFrBack(); AstShiftR* const newp = new AstShiftR{nodep->fileline(), opp, new AstConst(nodep->fileline(), amount)}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceModAnd(AstModDiv* nodep) { // Mod, but not ModS as not simple shift @@ -1914,8 +1904,7 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const opp = nodep->lhsp()->unlinkFrBack(); AstAnd* const newp = new AstAnd{nodep->fileline(), opp, new AstConst{nodep->fileline(), mask}}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceShiftOp(AstNodeBiop* nodep) { @@ -1994,8 +1983,7 @@ class ConstVisitor final : public VNVisitor { } newp->dtypeFrom(nodep); newp = new AstAnd{nodep->fileline(), newp, new AstConst{nodep->fileline(), mask}}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); // if (debug()) newp->dumpTree("- repShiftShift_new: "); iterate(newp); // Further reduce, either node may have more reductions. @@ -2347,10 +2335,9 @@ class ConstVisitor final : public VNVisitor { UASSERT_OBJ(valuep, nodep, "No value returned from simulation"); // Replace it AstNode* const newp = valuep->cloneTree(false); - newp->dtypeFrom(nodep); newp->fileline(nodep->fileline()); + nodep->replaceWithKeepDType(newp); UINFO(4, "Simulate->" << newp << endl); - nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } } @@ -2448,8 +2435,7 @@ class ConstVisitor final : public VNVisitor { if (!aRandp || !bRandp) return false; if (!aRandp->combinable(bRandp)) return false; UINFO(4, "Concat(Rand,Rand) => Rand: " << nodep << endl); - aRandp->dtypeFrom(nodep); // I.e. the total width - nodep->replaceWith(aRandp->unlinkFrBack()); + nodep->replaceWithKeepDType(aRandp->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -2459,8 +2445,7 @@ class ConstVisitor final : public VNVisitor { if (!aRandp) return false; if (aRandp->seedp()) return false; UINFO(4, "Sel(Rand) => Rand: " << nodep << endl); - aRandp->dtypeFrom(nodep); // I.e. the total width - nodep->replaceWith(aRandp->unlinkFrBack()); + nodep->replaceWithKeepDType(aRandp->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -2540,8 +2525,7 @@ class ConstVisitor final : public VNVisitor { new AstLogOr{nodep->fileline(), new AstLogNot{nodep->fileline(), rhsp->cloneTreePure(false)}, lhsp->cloneTreePure(false)}}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2632,8 +2616,7 @@ class ConstVisitor final : public VNVisitor { AstSel* const newp = new AstSel{nodep->fileline(), fromp, new AstConst{lsbp->fileline(), lsbp->toUInt() % fromp->width()}, widthp}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -2651,8 +2634,7 @@ class ConstVisitor final : public VNVisitor { cnt2p->unlinkFrBack(); AstReplicate* const newp = new AstReplicate{nodep->fileline(), from2p, cnt1p->toUInt() * cnt2p->toUInt()}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -2681,8 +2663,7 @@ class ConstVisitor final : public VNVisitor { // from1p->unlinkFrBack(); AstReplicate* const newp = new AstReplicate{nodep->fileline(), from1p, cnt1 + cnt2}; - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } @@ -2699,8 +2680,7 @@ class ConstVisitor final : public VNVisitor { fromp->lhsp(new AstSel{nodep->fileline(), bilhsp, lsbp->cloneTreePure(true), widthp->cloneTreePure(true)}); fromp->rhsp(new AstSel{nodep->fileline(), birhsp, lsbp, widthp}); - fromp->dtypeFrom(nodep); - nodep->replaceWith(fromp); + nodep->replaceWithKeepDType(fromp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void replaceSelIntoUniop(AstSel* nodep) { @@ -2713,8 +2693,7 @@ class ConstVisitor final : public VNVisitor { AstNodeExpr* const bilhsp = fromp->lhsp()->unlinkFrBack(); // fromp->lhsp(new AstSel{nodep->fileline(), bilhsp, lsbp, widthp}); - fromp->dtypeFrom(nodep); - nodep->replaceWith(fromp); + nodep->replaceWithKeepDType(fromp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index dae0a03db..168ea0d86 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -141,8 +141,7 @@ class PremitVisitor final : public VNVisitor { newp = new AstShiftRSOvr{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()}; } - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ad749136d..4a3771a79 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2066,8 +2066,7 @@ class WidthVisitor final : public VNVisitor { << nodep->warnMore() << "... Suggest try static cast"); } - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); userIterate(newp, m_vup); } @@ -2211,8 +2210,7 @@ class WidthVisitor final : public VNVisitor { if (m_vup->final()) { // CastSize not needed once sizes determined AstNode* const underp = nodep->lhsp()->unlinkFrBack(); - underp->dtypeFrom(nodep); - nodep->replaceWith(underp); + nodep->replaceWithKeepDType(underp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } // if (debug()) nodep->dumpTree("- CastSizeOut: "); @@ -7617,8 +7615,7 @@ class WidthVisitor final : public VNVisitor { break; } UINFO(6, " ReplaceWithUOrSVersion: " << nodep << " w/ " << newp << endl); - nodep->replaceWith(newp); - newp->dtypeFrom(nodep); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return newp; } @@ -7707,8 +7704,7 @@ class WidthVisitor final : public VNVisitor { break; } UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp << endl); - nodep->replaceWith(newp); - newp->dtypeFrom(nodep); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return newp; } diff --git a/src/V3WidthRemove.h b/src/V3WidthRemove.h index b879058f6..beaafdf04 100644 --- a/src/V3WidthRemove.h +++ b/src/V3WidthRemove.h @@ -37,8 +37,7 @@ class WidthRemoveVisitor final : public VNVisitor { void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { UINFO(6, " Replace " << nodep << " w/ " << newp << endl); - nodep->replaceWith(newp); - newp->dtypeFrom(nodep); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } From 69eb76ad6638a51cb58fe8142c434ad6a40e2667 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 5 May 2025 07:04:20 -0400 Subject: [PATCH 035/211] Fix constant propagation of post-expand stages (#5983). --- Changes | 2 +- docs/internals.rst | 2 +- src/V3Const.cpp | 40 ++++++------- src/V3Expand.cpp | 1 - test_regress/t/t_math_cv_bitop.out | 6 ++ test_regress/t/t_math_cv_bitop.py | 18 ++++++ test_regress/t/t_math_cv_bitop.v | 92 ++++++++++++++++++++++++++++++ 7 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 test_regress/t/t_math_cv_bitop.out create mode 100755 test_regress/t/t_math_cv_bitop.py create mode 100644 test_regress/t/t_math_cv_bitop.v diff --git a/Changes b/Changes index e4a4d1a5a..6404647b4 100644 --- a/Changes +++ b/Changes @@ -17,7 +17,7 @@ Verilator 5.037 devel * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). * Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore] -* Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972). +* Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972) (#5983). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] * Fix streaming to/from packed arrays (#5976). [Geza Lore] diff --git a/docs/internals.rst b/docs/internals.rst index 6825c0c21..acef93bc7 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1903,7 +1903,7 @@ find what made a line in the tree dumps): :: - watch AstNode::s_editCntGbl==#### + watch AstNode::s_editCntGbl=#### Then, when the watch fires, to break at every following change to that node: diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 864c3a283..96fb5b0a7 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1022,7 +1022,7 @@ class ConstVisitor final : public VNVisitor { } } if (ccastp) { - andp->replaceWith(ccastp); + andp->replaceWithKeepDType(ccastp); VL_DO_DANGLING(pushDeletep(andp), andp); return true; } @@ -1128,16 +1128,16 @@ class ConstVisitor final : public VNVisitor { const bool orRIsRedundant = checkBottomClear(orp->rhsp()); if (orLIsRedundant && orRIsRedundant) { - nodep->replaceWith( + nodep->replaceWithKeepDType( new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()}); VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } else if (orLIsRedundant) { - orp->replaceWith(orp->rhsp()->unlinkFrBack()); + orp->replaceWithKeepDType(orp->rhsp()->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(orp), orp); return false; // input node is still valid, keep going } else if (orRIsRedundant) { - orp->replaceWith(orp->lhsp()->unlinkFrBack()); + orp->replaceWithKeepDType(orp->lhsp()->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(orp), orp); return false; // input node is still valid, keep going } else { @@ -1204,8 +1204,8 @@ class ConstVisitor final : public VNVisitor { } if (newp) { + nodep->replaceWithKeepDType(newp); UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); - nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -1657,7 +1657,7 @@ class ConstVisitor final : public VNVisitor { AstNode* const newp = new AstConst{oldp->fileline(), AstConst::String{}, num}; if (debug() > 5) oldp->dumpTree("- const_old: "); if (debug() > 5) newp->dumpTree("- _new: "); - oldp->replaceWith(newp); + oldp->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(oldp), oldp); } //---------------------------------------- @@ -1676,9 +1676,9 @@ class ConstVisitor final : public VNVisitor { // NODE(..., CHILD(...)) -> REDOR(CHILD(...)) childp->unlinkFrBack(); if (childp->width1()) { - nodep->replaceWith(childp); + nodep->replaceWithKeepDType(childp); } else { - nodep->replaceWith(new AstRedOr{childp->fileline(), childp}); + nodep->replaceWithKeepDType(new AstRedOr{childp->fileline(), childp}); } VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -1767,7 +1767,7 @@ class ConstVisitor final : public VNVisitor { AstNodeBiop* const rp = VN_AS(nodep->rhsp()->unlinkFrBack(), NodeBiop); AstNodeExpr* const rlp = rp->lhsp()->unlinkFrBack(); AstNodeExpr* const rrp = rp->rhsp()->unlinkFrBack(); - nodep->replaceWith(lp); + nodep->replaceWithKeepDType(lp); if (operandsSame(llp, rlp)) { lp->lhsp(llp); lp->rhsp(nodep); @@ -1798,7 +1798,7 @@ class ConstVisitor final : public VNVisitor { AstNodeBiop* const rp = VN_AS(nodep->rhsp()->unlinkFrBack(), NodeBiop); AstNodeExpr* const rlp = rp->lhsp()->unlinkFrBack(); AstNodeExpr* const rrp = rp->rhsp()->unlinkFrBack(); - nodep->replaceWith(lp); + nodep->replaceWithKeepDType(lp); lp->lhsp(nodep); lp->rhsp(lrp); nodep->lhsp(llp); @@ -1824,7 +1824,7 @@ class ConstVisitor final : public VNVisitor { UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp << endl); - nodep->replaceWith(newselp); + nodep->replaceWithKeepDType(newselp); VL_DO_DANGLING(pushDeletep(lselp), lselp); VL_DO_DANGLING(pushDeletep(rselp), rselp); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -1850,7 +1850,7 @@ class ConstVisitor final : public VNVisitor { lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED); UINFO(5, "merged " << nodep << endl); VL_DO_DANGLING(pushDeletep(rp->unlinkFrBack()), rp); - nodep->replaceWith(lp->unlinkFrBack()); + nodep->replaceWithKeepDType(lp->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep), nodep); iterate(lp->lhsp()); iterate(lp->rhsp()); @@ -2043,7 +2043,7 @@ class ConstVisitor final : public VNVisitor { new AstConcat{rhs1p->fileline(), rhs1p, rhs2p}); } // if (debug()) pnewp->dumpTree("- conew: "); - nodep->replaceWith(newp); + nodep->replaceWith(newp); // dypep intentionally changing VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBack()), nextp); return true; @@ -2562,7 +2562,7 @@ class ConstVisitor final : public VNVisitor { newlsbp->dtypeFrom(widep); } AstSel* const newp = new AstSel{nodep->fileline(), fromp, newlsbp, widthp}; - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2576,12 +2576,12 @@ class ConstVisitor final : public VNVisitor { AstSel* const newp = new AstSel{nodep->fileline(), conLhsp, nodep->lsbConst() - conRhsp->width(), nodep->widthConst()}; - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); } else if (static_cast(nodep->msbConst()) < conRhsp->width()) { conRhsp->unlinkFrBack(); AstSel* const newp = new AstSel{nodep->fileline(), conRhsp, nodep->lsbConst(), nodep->widthConst()}; - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); } else { // Yuk, split between the two conRhsp->unlinkFrBack(); @@ -2592,7 +2592,7 @@ class ConstVisitor final : public VNVisitor { nodep->msbConst() - conRhsp->width() + 1}, new AstSel{nodep->fileline(), conRhsp, nodep->lsbConst(), conRhsp->width() - nodep->lsbConst()}}; - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); } VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -2718,7 +2718,7 @@ class ConstVisitor final : public VNVisitor { nodep->v3error("Illegal assignment of constant to unpacked array"); } else { AstNode* const fromp = nodep->fromp()->unlinkFrBack(); - nodep->replaceWith(fromp); + nodep->replaceWithKeepDType(fromp); if (VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) { // Strip off array to find what array references fromp->dtypeFrom( @@ -2771,12 +2771,12 @@ class ConstVisitor final : public VNVisitor { // This exception is fairly fragile, i.e. doesn't // support arrays of arrays or other stuff AstNode* const newp = valuep->cloneTree(false); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); did = true; } else if (nodep->varp()->isParam() && VN_IS(valuep, Unbounded)) { AstNode* const newp = valuep->cloneTree(false); - nodep->replaceWith(newp); + nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); did = true; } diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 395ba5e04..401923ef3 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -445,7 +445,6 @@ class ExpandVisitor final : public VNVisitor { midp = new AstCond{ nfl, // lsb % VL_EDATASIZE == 0 ? - new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, // 0 : new AstConst{nfl, zero}, diff --git a/test_regress/t/t_math_cv_bitop.out b/test_regress/t/t_math_cv_bitop.out new file mode 100644 index 000000000..d4536207b --- /dev/null +++ b/test_regress/t/t_math_cv_bitop.out @@ -0,0 +1,6 @@ +one 'd=-1 'b=1 +ort 'd=-1 'b=1 +tmp 'd= -1 'b=11111111 +out63 'd= 1 'b=000000000000000000000001 +out63 'd= 1 'b=000000000000000000000001 +*-* All Finished *-* diff --git a/test_regress/t/t_math_cv_bitop.py b/test_regress/t/t_math_cv_bitop.py new file mode 100755 index 000000000..dcb1ff476 --- /dev/null +++ b/test_regress/t/t_math_cv_bitop.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"]) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_math_cv_bitop.v b/test_regress/t/t_math_cv_bitop.v new file mode 100644 index 000000000..9a4cbcf6a --- /dev/null +++ b/test_regress/t/t_math_cv_bitop.v @@ -0,0 +1,92 @@ +// 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 + +`define stop $stop +`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module sub ( + input wire clock_4, + input wire clock_8, + output wire [28:5] out63 +); + + reg [28:0] reg_12; + reg [28:22] reg_24; + + wire _0558_ = | reg_24[26:25]; // reg_24 = 0 or 1100110 ---> _0558_ == 0 + wire [28:0] _0670_ = _0558_ ? reg_12 : 29'h00000f93; // _0558_ == 0 ---> _0670_ == 29'h00000f93 + wire [28:0] _0399_= - _0670_; // _0670_ == 29'h00000f93 ---> _0399_ = 29'b11111111111111111000001101101 + wire _0085_ = ~ _0399_[2]; // _0399_[2] == 1 ---> _0085_ == 0 + wire [28:0] _0769_; + assign { _0769_[28:3], _0769_[1:0] } = { _0399_[28:3], _0399_[1:0] }; // _0769_ != 0 + assign _0769_[2] = _0085_; + + // verilator lint_off WIDTH + wire _0305_ = ! _0769_; // _0769_ != 0 ---> _0305_ == 0 + wire [23:0] _0306_ = ! _0305_; // _0305_ == 0 ---> _0306_ == 1 + // verilator lint_on WIDTH + + assign out63 = _0306_; // out63 == 1 + + always @(posedge clock_4, posedge clock_8) + if (clock_8) reg_12 <= 29'h00000066; + else reg_12 <= { reg_12[28:27], 25'h0000001, reg_12[1:0] }; + + always @(posedge clock_4, posedge clock_8) + if (clock_8) reg_24 <= 7'h66; + else reg_24 <= reg_24; + +endmodule + +module t; + reg clock_4; + reg clock_8; + wire signed [28:5] out63; + reg signed [7:0] tmp = -1; + reg signed [0:0] one = 1; + reg signed [0:0] onert = 1; + + sub sub ( + .clock_4 (clock_4), + .clock_8 (clock_8), + .out63 (out63) + ); + + + initial begin + // All simulators agree: 1'sb1 really shows as decimal -1 + $display("one 'd=%d 'b=%b", one, one); +`ifdef VERILATOR + onert = $c(1); +`endif + $display("ort 'd=%d 'b=%b", onert, onert); + + $display("tmp 'd=%d 'b=%b", tmp, tmp); + + clock_4 = 0; + clock_8 = 0; + #2000; + + sub.reg_24 = 0; + sub.reg_12 = 0; + #2000; + + clock_4 = 0; + clock_8 = 0; + #10; + $display("out63 'd=%d 'b=%b", out63, out63); + + #2000; + clock_4 = 1; + clock_8 = 1; + #10; + $display("out63 'd=%d 'b=%b", out63, out63); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 49e5c305a4b0fec456a6cc6ab863e8ac90a2a113 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 5 May 2025 18:35:50 -0400 Subject: [PATCH 036/211] Tests: Split and rename t_parse_sync_bad --- test_regress/t/t_parse_sync_bad.out | 11 +++++++ ...source_sync_bad.py => t_parse_sync_bad.py} | 0 test_regress/t/t_parse_sync_bad.v | 29 +++++++++++++++++++ test_regress/t/t_parse_sync_bad2.out | 8 +++++ test_regress/t/t_parse_sync_bad2.py | 16 ++++++++++ ..._source_sync_bad.v => t_parse_sync_bad2.v} | 11 ------- test_regress/t/t_source_sync_bad.out | 17 ----------- 7 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 test_regress/t/t_parse_sync_bad.out rename test_regress/t/{t_source_sync_bad.py => t_parse_sync_bad.py} (100%) create mode 100644 test_regress/t/t_parse_sync_bad.v create mode 100644 test_regress/t/t_parse_sync_bad2.out create mode 100755 test_regress/t/t_parse_sync_bad2.py rename test_regress/t/{t_source_sync_bad.v => t_parse_sync_bad2.v} (75%) delete mode 100644 test_regress/t/t_source_sync_bad.out diff --git a/test_regress/t/t_parse_sync_bad.out b/test_regress/t/t_parse_sync_bad.out new file mode 100644 index 000000000..ad7479297 --- /dev/null +++ b/test_regress/t/t_parse_sync_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_parse_sync_bad.v:19:22: syntax error, unexpected IDENTIFIER, expecting "'{" + 19 | pkg::cls::defi invalid; + | ^~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_parse_sync_bad.v:25:14: syntax error, unexpected /*verilator clocker*/, expecting ',' or ';' + 25 | logic clk /*verilator clocker*/ ; + | ^~~~~~~~~~~~~~~~~~~~~ +%Error: t/t_parse_sync_bad.v:29:1: syntax error, unexpected endmodule + 29 | endmodule + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_source_sync_bad.py b/test_regress/t/t_parse_sync_bad.py similarity index 100% rename from test_regress/t/t_source_sync_bad.py rename to test_regress/t/t_parse_sync_bad.py diff --git a/test_regress/t/t_parse_sync_bad.v b/test_regress/t/t_parse_sync_bad.v new file mode 100644 index 000000000..579db166f --- /dev/null +++ b/test_regress/t/t_parse_sync_bad.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Dan Petrisko. +// SPDX-License-Identifier: CC0-1.0 + +package pkg; + class cls; + typedef unknown defu; + typedef int defi; + endclass +endpackage + +module t; + task tsk; + begin + valid1 = 5; // valid statement + + pkg::cls::defi invalid; // invalid statement + end + endtask +endmodule + +typedef struct packed { + logic clk /*verilator clocker*/; + logic data; +} ss_s; + +endmodule diff --git a/test_regress/t/t_parse_sync_bad2.out b/test_regress/t/t_parse_sync_bad2.out new file mode 100644 index 000000000..fdfd58413 --- /dev/null +++ b/test_regress/t/t_parse_sync_bad2.out @@ -0,0 +1,8 @@ +%Error: t/t_parse_sync_bad2.v:17:16: syntax error, unexpected IDENTIFIER + 17 | Invalid1 invalid1; + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_parse_sync_bad2.v:20:16: syntax error, unexpected IDENTIFIER + 20 | Invalid2 invalid2; + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_parse_sync_bad2.py b/test_regress/t/t_parse_sync_bad2.py new file mode 100755 index 000000000..e33e10acf --- /dev/null +++ b/test_regress/t/t_parse_sync_bad2.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_source_sync_bad.v b/test_regress/t/t_parse_sync_bad2.v similarity index 75% rename from test_regress/t/t_source_sync_bad.v rename to test_regress/t/t_parse_sync_bad2.v index 32b7559ab..eb94043b8 100644 --- a/test_regress/t/t_source_sync_bad.v +++ b/test_regress/t/t_parse_sync_bad2.v @@ -18,17 +18,6 @@ module t; pkg::cls::defi valid1; // valid declaration pkg::cls::defu valid2; // valid declaration Invalid2 invalid2; // invalid declaration - - valid1 = 5; // valid statement - - pkg::cls::defi invalid; // invalid statement end endtask endmodule - -typedef struct packed { - logic clk /*verilator clocker*/; - logic data; -} ss_s; - -endmodule diff --git a/test_regress/t/t_source_sync_bad.out b/test_regress/t/t_source_sync_bad.out deleted file mode 100644 index 61583aa98..000000000 --- a/test_regress/t/t_source_sync_bad.out +++ /dev/null @@ -1,17 +0,0 @@ -%Error: t/t_source_sync_bad.v:17:16: syntax error, unexpected IDENTIFIER - 17 | Invalid1 invalid1; - | ^~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_source_sync_bad.v:20:16: syntax error, unexpected IDENTIFIER - 20 | Invalid2 invalid2; - | ^~~~~~~~ -%Error: t/t_source_sync_bad.v:24:22: syntax error, unexpected IDENTIFIER, expecting "'{" - 24 | pkg::cls::defi invalid; - | ^~~~~~~ -%Error: t/t_source_sync_bad.v:30:14: syntax error, unexpected /*verilator clocker*/, expecting ',' or ';' - 30 | logic clk /*verilator clocker*/ ; - | ^~~~~~~~~~~~~~~~~~~~~ -%Error: t/t_source_sync_bad.v:34:1: syntax error, unexpected endmodule - 34 | endmodule - | ^~~~~~~~~ -%Error: Exiting due to From fe562d4715497283fc704362f7829268096ff75e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 5 May 2025 19:54:52 -0400 Subject: [PATCH 037/211] Internals: Move Stream dtype conversion to V3Width, towards future parser --- src/V3Ast.h | 4 +++- src/V3Width.cpp | 14 ++++++++++++++ src/verilog.y | 8 ++------ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 37ec30133..241eb0383 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -458,6 +458,7 @@ public: ILLEGAL, // DIM_BITS, // V3Const converts to constant + DIM_BITS_OR_NUMBER, // V3Const converts to constant DIM_DIMENSIONS, // V3Width converts to constant DIM_HIGH, // V3Width processes DIM_INCREMENT, // V3Width processes @@ -500,7 +501,8 @@ public: // clang-format off static const char* const names[] = { "%E-AT", - "DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT", + "DIM_BITS", "DIM_BITS_OR_NUMBER", "DIM_DIMENSIONS", + "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT", "DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS", "DT_PUBLIC", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4a3771a79..8b9383f61 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1672,6 +1672,20 @@ class WidthVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); break; } + case VAttrType::DIM_BITS_OR_NUMBER: { + // If dtype, compute DIM_BITS, else take expression as a number to use + if (VN_IS(nodep->fromp(), NodeExpr)) { + nodep->replaceWith(nodep->fromp()->unlinkFrBack()); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else { + AstNode* newp = new AstAttrOf{nodep->fileline(), VAttrType::DIM_BITS, + nodep->fromp()->unlinkFrBack()}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + userIterateAndNext(newp, WidthVP{SELF, BOTH}.p()); // Convert AttrOf + } + return; + } case VAttrType::DIM_BITS: case VAttrType::DIM_HIGH: case VAttrType::DIM_INCREMENT: diff --git a/src/verilog.y b/src/verilog.y index 126c2089c..80ed887c1 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -5437,13 +5437,9 @@ streaming_concatenation: // ==IEEE: streaming_concatenation | '{' yP_SRIGHT stream_concatenation '}' { $$ = new AstStreamR{$2, $3, new AstConst{$2, 1}}; } | '{' yP_SLEFT stream_expressionOrDataType stream_concatenation '}' - { AstNodeExpr* const bitsp = VN_IS($3, NodeExpr) ? VN_AS($3, NodeExpr) - : new AstAttrOf{$1, VAttrType::DIM_BITS, $3}; - $$ = new AstStreamL{$2, $4, bitsp}; } + { $$ = new AstStreamL{$2, $4, new AstAttrOf{$1, VAttrType::DIM_BITS_OR_NUMBER, $3}}; } | '{' yP_SRIGHT stream_expressionOrDataType stream_concatenation '}' - { AstNodeExpr* const bitsp = VN_IS($3, NodeExpr) ? VN_AS($3, NodeExpr) - : new AstAttrOf{$1, VAttrType::DIM_BITS, $3}; - $$ = new AstStreamR{$2, $4, bitsp}; } + { $$ = new AstStreamR{$2, $4, new AstAttrOf{$1, VAttrType::DIM_BITS_OR_NUMBER, $3}}; } ; stream_concatenation: // ==IEEE: stream_concatenation From 2ed754d5ea8d2f2d8df2a5dc4f684af690449af1 Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Tue, 6 May 2025 18:00:17 +0900 Subject: [PATCH 038/211] Fix Inconsistent assignment error by split-var (#5984) (#5988) --- src/V3SplitVar.cpp | 10 ++------- test_regress/t/t_split_var_types.v | 33 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 58c978d89..7261a877e 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -919,14 +919,8 @@ public: points.emplace_back(ref.lsb(), false); // Start of a region points.emplace_back(ref.msb() + 1, true); // End of a region } - int bit_hi, bit_lo; - if (basicp() == dtypep) { - bit_hi = basicp()->hi(); - bit_lo = basicp()->lo(); - } else { // packed struct, packed array. lo is 0 - bit_hi = dtypep->width() - 1; - bit_lo = 0; - } + const int bit_lo = basicp()->lo(); + const int bit_hi = bit_lo + dtypep->width() - 1; if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here int lsb = bit_hi + 1; int msb = bit_lo - 1; diff --git a/test_regress/t/t_split_var_types.v b/test_regress/t/t_split_var_types.v index 148c6ae84..cb94f7002 100644 --- a/test_regress/t/t_split_var_types.v +++ b/test_regress/t/t_split_var_types.v @@ -10,13 +10,20 @@ module t(/*AUTOARG*/ ); input clk; + logic [7:0] data = 0; // Test loop always @ (posedge clk) begin - $write("*-* All Finished *-*\n"); - $finish; + if (data != 15) begin + data <= data + 8'd1; + end else begin + $write("*-* All Finished *-*\n"); + $finish; + end end + bug5782 u_bug5782(.data_out()); + bug5984 u_bug5984(.in(data)); endmodule @@ -29,3 +36,25 @@ module bug5782 ( data_out = data[7]; end endmodule + +// #5984 inconsistent assignment due to wrong bit range calculation. +module bug5984 ( + input logic [1:0][3:0] in + ); + + logic [1:0][5:2] internal; + + for (genvar dim1 = 0; dim1 < 2; dim1++) begin + for (genvar dim2 = 0; dim2 < 4; dim2++) begin + assign internal[dim1][dim2+2] = in[dim1][dim2]; + end + end + + for (genvar dim1 = 0; dim1 < 2; dim1++) begin + for (genvar dim2 = 0; dim2 < 4; dim2++) begin + always_ff @(negedge internal[dim1][dim2+2]) begin + $display("%0b", internal[dim1][dim2+2]); + end + end + end +endmodule From da5eb620bf3a5699555de9ea21e1b78459f4af21 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 6 May 2025 06:34:49 -0400 Subject: [PATCH 039/211] Internals: Move timeunit to pragma, in prep for future parser. No user-functional change intended. --- src/V3Ast.h | 1 + src/V3AstNodeOther.h | 6 ++++++ src/V3LinkLevel.cpp | 43 +++++++++++++++++++++++++++++-------------- src/V3ParseImp.cpp | 19 ++++++++----------- src/V3ParseImp.h | 4 ++-- src/verilog.y | 9 +++------ 6 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 241eb0383..fee64ac13 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -336,6 +336,7 @@ public: NO_INLINE_TASK, PUBLIC_MODULE, PUBLIC_TASK, + TIMEUNIT_SET, UNROLL_DISABLE, UNROLL_FULL, FULL_CASE, diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 7d0180908..5aa5b6a0f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1494,18 +1494,24 @@ public: }; class AstPragma final : public AstNode { const VPragmaType m_pragType; // Type of pragma + const VTimescale m_timescale; // For TIMEUNIT_SET public: // Pragmas don't result in any output code, they're just flags that affect // other processing in verilator. AstPragma(FileLine* fl, VPragmaType pragType) : ASTGEN_SUPER_Pragma(fl) , m_pragType{pragType} {} + AstPragma(FileLine* fl, VPragmaType pragType, const VTimescale& timescale) + : ASTGEN_SUPER_Pragma(fl) + , m_pragType{pragType} + , m_timescale(timescale) {} ASTGEN_MEMBERS_AstPragma; VPragmaType pragType() const { return m_pragType; } // *=type of the pragma bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* samep) const override { return pragType() == VN_DBG_AS(samep, Pragma)->pragType(); } + VTimescale timescale() const { return m_timescale; } }; class AstPropSpec final : public AstNode { // A clocked property diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index b982dbbe0..e90eda0a0 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -88,9 +88,24 @@ void V3LinkLevel::modSortByLevel() { void V3LinkLevel::timescaling(const ModVec& mods) { // Timescale determination const AstNodeModule* modTimedp = nullptr; - VTimescale unit(VTimescale::NONE); + VTimescale unit{VTimescale::NONE}; + + // Move timeunit attributes from parse to module unit + // Grammar only allows timeunit as module_item, so no need to recurse full tree + for (AstNodeModule* modp : mods) { + for (AstNode *nextp, *childp = modp->stmtsp(); childp; childp = nextp) { + nextp = childp->nextp(); + if (AstPragma* pragp = VN_CAST(childp, Pragma)) { + if (pragp->pragType() == VPragmaType::TIMEUNIT_SET) { + modp->timeunit(pragp->timescale()); + VL_DO_DANGLING(pragp->unlinkFrBack()->deleteTree(), pragp); + } + } + } + } // Use highest level module as default unit - already sorted in proper order - for (const auto& modp : mods) { + // Combine timing into later modules + for (AstNodeModule* modp : mods) { if (!modTimedp && !modp->timeunit().isNone()) { modTimedp = modp; unit = modTimedp->timeunit(); @@ -106,24 +121,24 @@ void V3LinkLevel::timescaling(const ModVec& mods) { if (!upkgp->timeunit().isNone()) dunitTimed = true; } - for (AstNodeModule* nodep : mods) { - if (!v3Global.opt.timeOverrideUnit().isNone()) nodep->timeunit(unit); - if (nodep->timeunit().isNone()) { + for (AstNodeModule* modp : mods) { + if (!v3Global.opt.timeOverrideUnit().isNone()) modp->timeunit(unit); + if (modp->timeunit().isNone()) { if (modTimedp // Got previous && !dunitTimed && ( // unit doesn't already include an override v3Global.opt.timeOverrideUnit().isNone() && v3Global.opt.timeDefaultUnit().isNone()) - && nodep->timescaleMatters()) { - nodep->v3warn(TIMESCALEMOD, - "Timescale missing on this module as other modules have " - "it (IEEE 1800-2023 3.14.2.3)\n" - << nodep->warnContextPrimary() << '\n' - << modTimedp->warnOther() - << "... Location of module with timescale\n" - << modTimedp->warnContextSecondary()); + && modp->timescaleMatters()) { + modp->v3warn(TIMESCALEMOD, + "Timescale missing on this module as other modules have " + "it (IEEE 1800-2023 3.14.2.3)\n" + << modp->warnContextPrimary() << '\n' + << modTimedp->warnOther() + << "... Location of module with timescale\n" + << modTimedp->warnContextSecondary()); } - nodep->timeunit(unit); + modp->timeunit(unit); } } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index d71d6a090..8e6613fe1 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -126,8 +126,8 @@ void V3ParseImp::lexTimescaleParse(FileLine* fl, const char* textp) { m_timeLastUnit = v3Global.opt.timeComputeUnit(unit); v3Global.rootp()->timeprecisionMerge(fl, prec); } -void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, double unitVal, - bool precSet, double precVal) { +AstPragma* V3ParseImp::createTimescale(FileLine* fl, bool unitSet, double unitVal, bool precSet, + double precVal) { VTimescale unit{VTimescale::NONE}; if (unitSet) { bool bad; @@ -146,16 +146,13 @@ void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, d fl->v3error("timeprecision illegal value"); } } - if (!unit.isNone()) { - unit = v3Global.opt.timeComputeUnit(unit); - if (modp) { - modp->timeunit(unit); - } else { - v3Global.rootp()->timeunit(unit); - unitPackage(fl)->timeunit(unit); - } - } v3Global.rootp()->timeprecisionMerge(fl, prec); + if (unit.isNone()) { + return nullptr; + } else { + unit = v3Global.opt.timeComputeUnit(unit); + return new AstPragma{fl, VPragmaType::TIMEUNIT_SET, unit}; + } } void V3ParseImp::lexVerilatorCmtLintSave(const FileLine* fl) { m_lexLintState.push_back(*fl); } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index e6ebcbc92..5fcf49490 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -181,8 +181,8 @@ public: void tagNodep(AstNode* nodep) { m_tagNodep = nodep; } AstNode* tagNodep() const { return m_tagNodep; } void lexTimescaleParse(FileLine* fl, const char* textp) VL_MT_DISABLED; - void timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, double unitVal, - bool precSet, double precVal) VL_MT_DISABLED; + AstPragma* createTimescale(FileLine* fl, bool unitSet, double unitVal, bool precSet, + double precVal) VL_MT_DISABLED; VTimescale timeLastUnit() const { return m_timeLastUnit; } void lexFileline(FileLine* fl) { m_lexFileline = fl; } diff --git a/src/verilog.y b/src/verilog.y index 80ed887c1..1a4940496 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1227,14 +1227,11 @@ description: // ==IEEE: description timeunits_declaration: // ==IEEE: timeunits_declaration yTIMEUNIT yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), true, $2, false, 0); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, true, $2, false, 0); } | yTIMEUNIT yaTIMENUM '/' yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), true, $2, true, $4); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, true, $2, true, $4); } | yTIMEPRECISION yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), false, 0, true, $2); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, false, 0, true, $2); } ; //********************************************************************** From d5f773f385a4db4ec63f6b3993017ead784d1cb5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 6 May 2025 08:03:30 -0400 Subject: [PATCH 040/211] Internals: Move hasParameterList away from symbol table. No functional change intended. --- src/V3ParseImp.h | 1 + src/V3ParseSym.h | 7 ------- src/verilog.y | 17 ++++++++++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 5fcf49490..22778a657 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -111,6 +111,7 @@ struct V3ParseBisonYYSType final { AstNode* scp; // Symbol table scope for future lookups int token; // Read token, aka tok VBaseOverride baseOverride; + bool flag = false; // Passed up some rules union { V3Number* nump; string* strp; diff --git a/src/V3ParseSym.h b/src/V3ParseSym.h index 1f24cd76c..d1911a8bd 100644 --- a/src/V3ParseSym.h +++ b/src/V3ParseSym.h @@ -130,13 +130,6 @@ public: UASSERT_OBJ(!m_sympStack.empty(), nodep, "symbol stack underflow"); m_symCurrentp = m_sympStack.back(); } - AstNodeModule* findTopNodeModule(FileLine* fl, bool requireNoneNull = true) { - for (VSymEnt* const symp : vlstd::reverse_view(m_sympStack)) { - if (AstNodeModule* const modp = VN_CAST(symp->nodep(), NodeModule)) return modp; - } - if (requireNoneNull) fl->v3fatalSrc("fail to find current module"); - return nullptr; - } void showUpward() { // LCOV_EXCL_START UINFO(1, "ParseSym Stack:\n"); for (VSymEnt* const symp : vlstd::reverse_view(m_sympStack)) { diff --git a/src/verilog.y b/src/verilog.y index 1a4940496..5c69afffb 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1379,6 +1379,7 @@ module_declaration: // ==IEEE: module_declaration modFront importsAndParametersE portsStarE ';' /*cont*/ module_itemListE yENDMODULE endLabelE { $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc + $1->hasParameterList($2); if ($2) $1->addStmtsp($2); if ($3) $1->addStmtsp($3); if ($5) $1->addStmtsp($5); @@ -1414,8 +1415,11 @@ modFront: importsAndParametersE: // IEEE: common part of module_declaration, interface_declaration, program_declaration // // { package_import_declaration } [ parameter_port_list ] - parameter_port_listE { $$ = $1; } - | package_import_declarationList parameter_port_listE { $$ = addNextNull($1, $2); } + parameter_port_listE + { $$ = $1; $$ = $1; } // hasParameterList + | package_import_declarationList parameter_port_listE + { $$ = addNextNull($1, $2); + $$ = $2; } // hasParameterList ; udpFront: @@ -1460,17 +1464,17 @@ parameter_value_assignmentClass: // IEEE: parameter_value_assignment (for ; parameter_port_listE: // IEEE: parameter_port_list + empty == parameter_value_assignment - /* empty */ { $$ = nullptr; } + /* empty */ { $$ = nullptr; $$ = false; } // hasParameterList | '#' '(' ')' { $$ = nullptr; - SYMP->findTopNodeModule($1)->hasParameterList(true); } + $$ = true; } // hasParameterList // // IEEE: '#' '(' list_of_param_assignments { ',' parameter_port_declaration } ')' // // IEEE: '#' '(' parameter_port_declaration { ',' parameter_port_declaration } ')' // // Can't just do that as "," conflicts with between vars and between stmts, so // // split into pre-comma and post-comma parts | '#' '(' { VARRESET_LIST(GPARAM); - SYMP->findTopNodeModule($1)->hasParameterList(true); GRAMMARP->m_pinAnsi = true; } /*cont*/ paramPortDeclOrArgList ')' { $$ = $4; + $$ = true; // hasParameterList VARRESET_NONLIST(UNKNOWN); GRAMMARP->m_pinAnsi = false; } // // Note legal to start with "a=b" with no parameter statement @@ -1685,6 +1689,7 @@ interface_declaration: // IEEE: interface_declaration + interface_nonan { if ($2) $1->addStmtsp($2); if ($3) $1->addStmtsp($3); if ($5) $1->addStmtsp($5); + $1->hasParameterList($2); SYMP->popScope($1); } | yEXTERN intFront parameter_port_listE portsStarE ';' { BBUNSUP($1, "Unsupported: extern interface"); } @@ -1769,6 +1774,7 @@ program_declaration: // IEEE: program_declaration + program_nonansi_h pgmFront parameter_port_listE portsStarE ';' /*cont*/ program_itemListE yENDPROGRAM endLabelE { $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc + $1->hasParameterList($2); if ($2) $1->addStmtsp($2); if ($3) $1->addStmtsp($3); if ($5) $1->addStmtsp($5); @@ -7307,6 +7313,7 @@ class_declaration: // ==IEEE: part of class_declaration // // new class scope correct via classFront classFront parameter_port_listE classExtendsE classImplementsE ';' /*mid*/ { // Allow resolving types declared in base extends class + $1->hasParameterList($2); if ($3) SYMP->importExtends($3); } /*cont*/ class_itemListEnd endLabelE From b099d6fe639dee08a80e27e4e1fd3de48ca0f658 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 6 May 2025 20:39:17 -0400 Subject: [PATCH 041/211] Fix implicit dtype numbering to be per-module. Internals: Remove use of parser in implicit dtype numbering. --- src/V3AstNodeDType.h | 8 +------- src/V3LinkParse.cpp | 18 ++++++++++++------ src/V3ParseImp.cpp | 7 ++++++- src/verilog.y | 12 ++++++------ test_regress/t/t_typename.out | 2 +- test_regress/t/t_typename.py | 2 +- 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 81c93c04c..376b30011 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -666,16 +666,12 @@ class AstDefImplicitDType final : public AstNodeDType { // After link, these become typedefs // @astgen op1 := childDTypep : Optional[AstNodeDType] string m_name; - void* m_containerp; // In what scope is the name unique, so we can know what are duplicate - // definitions (arbitrary value) const int m_uniqueNum; public: - AstDefImplicitDType(FileLine* fl, const string& name, void* containerp, VFlagChildDType, - AstNodeDType* dtp) + AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) : ASTGEN_SUPER_DefImplicitDType(fl) , m_name{name} - , m_containerp{containerp} , m_uniqueNum{uniqueNumInc()} { childDTypep(dtp); // Only for parser dtypep(nullptr); // V3Width will resolve @@ -683,7 +679,6 @@ public: AstDefImplicitDType(const AstDefImplicitDType& other) : AstNodeDType(other) , m_name(other.m_name) - , m_containerp(other.m_containerp) , m_uniqueNum(uniqueNumInc()) {} ASTGEN_MEMBERS_AstDefImplicitDType; int uniqueNum() const { return m_uniqueNum; } @@ -696,7 +691,6 @@ public: AstNodeDType* subDTypep() const override VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); } - void* containerp() const { return m_containerp; } // METHODS // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index d2860ff58..c48830a09 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -42,7 +42,7 @@ class LinkParseVisitor final : public VNVisitor { const VNUser2InUse m_inuser2; // TYPES - using ImplTypedefMap = std::map, AstTypedef*>; + using ImplTypedefMap = std::map; // STATE AstVar* m_varp = nullptr; // Variable we're under @@ -498,14 +498,14 @@ class LinkParseVisitor final : public VNVisitor { cleanFileline(nodep); UINFO(8, " DEFIMPLICIT " << nodep << endl); // Must remember what names we've already created, and combine duplicates - // so that for "var enum {...} a,b" a & b will share a common typedef - // Unique name space under each containerp() so that an addition of + // so that for "var enum {...} a,b" a & b will share a common typedef. + // Change to unique name space per module so that an addition of // a new type won't change every verilated module. AstTypedef* defp = nullptr; - const ImplTypedefMap::iterator it - = m_implTypedef.find(std::make_pair(nodep->containerp(), nodep->name())); + const ImplTypedefMap::iterator it = m_implTypedef.find(nodep->name()); if (it != m_implTypedef.end()) { defp = it->second; + UINFO(9, "Reused impltypedef " << nodep << " --> " << defp << endl); } else { // Definition must be inserted right after the variable (etc) that needed it // AstVar, AstTypedef, AstNodeFTask are common containers @@ -526,7 +526,11 @@ class LinkParseVisitor final : public VNVisitor { } else { defp = new AstTypedef{nodep->fileline(), nodep->name(), nullptr, VFlagChildDType{}, dtypep}; - m_implTypedef.emplace(std::make_pair(nodep->containerp(), defp->name()), defp); + m_implTypedef.emplace(defp->name(), defp); + // Rename so that name doesn't change if a type is added/removed elsewhere + // But the m_implTypedef is stil by old name so we can find it for next new lookups + defp->name("__typeimpmod" + cvtToStr(m_implTypedef.size())); + UINFO(9, "New impltypedef " << defp << endl); backp->addNextHere(defp); } } @@ -618,6 +622,7 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_genblkAbove); VL_RESTORER(m_genblkNum); VL_RESTORER(m_beginDepth); + VL_RESTORER(m_implTypedef); VL_RESTORER(m_lifetime); VL_RESTORER(m_lifetimeAllowed); { @@ -630,6 +635,7 @@ class LinkParseVisitor final : public VNVisitor { m_genblkAbove = 0; m_genblkNum = 0; m_beginDepth = 0; + m_implTypedef.clear(); m_valueModp = nodep; m_lifetime = nodep->lifetime(); m_lifetimeAllowed = VN_IS(nodep, Class); diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 8e6613fe1..b33972145 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -749,7 +749,12 @@ int V3ParseImp::tokenToBison() { // V3ParseBisonYYSType functions std::ostream& operator<<(std::ostream& os, const V3ParseBisonYYSType& rhs) { - os << "TOKEN {" << rhs.fl->filenameLetters() << rhs.fl->asciiLineCol() << "}"; + os << "TOKEN {"; + if (VL_UNCOVERABLE(!rhs.fl)) + os << "%E-null-fileline"; + else + os << rhs.fl->filenameLetters() << rhs.fl->asciiLineCol(); + os << "}"; os << "=" << rhs.token << " " << V3ParseImp::tokenName(rhs.token); if (rhs.token == yaID__ETC // || rhs.token == yaID__CC // diff --git a/src/verilog.y b/src/verilog.y index 5c69afffb..08025f727 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -108,7 +108,7 @@ public: int m_pinNum = -1; // Pin number currently parsing std::stack m_pinStack; // Queue of pin numbers being parsed - static int s_modTypeImpNum; // Implicit type number, incremented each module + static int s_typeImpNum; // Implicit type number, incremented each module // CONSTRUCTORS V3ParseGrammar() {} @@ -300,7 +300,7 @@ public: const VBasicDTypeKwd LOGIC = VBasicDTypeKwd::LOGIC; // Shorthand "LOGIC" const VBasicDTypeKwd LOGIC_IMPLICIT = VBasicDTypeKwd::LOGIC_IMPLICIT; -int V3ParseGrammar::s_modTypeImpNum = 0; +int V3ParseGrammar::s_typeImpNum = 0; //====================================================================== // Macro functions @@ -2233,12 +2233,12 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_ty | struct_unionDecl packed_dimensionListE { $$ = GRAMMARP->createArray( new AstDefImplicitDType{$1->fileline(), - "__typeimpsu" + cvtToStr(GRAMMARP->s_modTypeImpNum++), - SYMP, VFlagChildDType{}, $1}, $2, true); } + "__typeimpsu" + cvtToStr(GRAMMARP->s_typeImpNum++), + VFlagChildDType{}, $1}, $2, true); } | enumDecl { $$ = new AstDefImplicitDType{$1->fileline(), - "__typeimpenum" + cvtToStr(GRAMMARP->s_modTypeImpNum++), - SYMP, VFlagChildDType{}, $1}; } + "__typeimpenum" + cvtToStr(GRAMMARP->s_typeImpNum++), + VFlagChildDType{}, $1}; } | ySTRING { $$ = new AstBasicDType{$1, VBasicDTypeKwd::STRING}; } | yCHANDLE diff --git a/test_regress/t/t_typename.out b/test_regress/t/t_typename.out index 133f56582..f8ebfd10d 100644 --- a/test_regress/t/t_typename.out +++ b/test_regress/t/t_typename.out @@ -20,7 +20,7 @@ "int$[$:3]" ==? "int$[$:3]" "bit$[]" ==? "bit$[]" -"enum{A=32'h0;B=32'h1;C=32'h63;}A::__typeimpenum1" ==? "enum{A=32'sd0,B=32'sd1,C=32'sd99}A::" +"enum{A=32'h0;B=32'h1;C=32'h63;}A::__typeimpmod1" ==? "enum{A=32'sd0,B=32'sd1,C=32'sd99}A::" "struct{bit A;bit B;}t.AB_t" ==? "struct{bit A;bit B;}" "struct{bit A;bit B;}t.AB_t$[0:9]" ==? "struct{bit A;bit B;}top.AB_t$[0:9]" "union{bit A;bit B;}t.UAB_t" ==? "union{bit A;bit B;}" diff --git a/test_regress/t/t_typename.py b/test_regress/t/t_typename.py index ab5dca066..1a1bebba6 100755 --- a/test_regress/t/t_typename.py +++ b/test_regress/t/t_typename.py @@ -9,7 +9,7 @@ import vltest_bootstrap -test.scenarios('simulator') +test.scenarios('simulator_st') test.compile() From bc3bf6ab5ef7e40feb15f100272edf1917d0b7d2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 6 May 2025 21:11:34 -0400 Subject: [PATCH 042/211] Tests: Add t_param_type_bad3 --- src/V3LinkDot.cpp | 4 ++-- src/V3Param.cpp | 4 ++-- test_regress/t/t_param_type_bad3.out | 5 +++++ test_regress/t/t_param_type_bad3.py | 20 ++++++++++++++++++++ test_regress/t/t_param_type_bad3.v | 10 ++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 test_regress/t/t_param_type_bad3.out create mode 100755 test_regress/t/t_param_type_bad3.py create mode 100644 test_regress/t/t_param_type_bad3.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 39e8d4510..5ace4d040 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3341,8 +3341,8 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } - } else if (AstConstraint* const consp = VN_CAST(foundp->nodep(), Constraint)) { - AstNode* const newp = new AstConstraintRef{nodep->fileline(), nullptr, consp}; + } else if (AstConstraint* const defp = VN_CAST(foundp->nodep(), Constraint)) { + AstNode* const newp = new AstConstraintRef{nodep->fileline(), nullptr, defp}; nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); ok = true; diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 09092af24..bfee3e4fe 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -928,7 +928,7 @@ class ParamProcessor final { for (auto* stmtp = srcModpr->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstParamTypeDType* dtypep = VN_CAST(stmtp, ParamTypeDType)) { - if (VN_IS(dtypep->subDTypep(), VoidDType)) { + if (VN_IS(dtypep->skipRefp(), VoidDType)) { nodep->v3error( "Class parameter type without default value is never given value" << " (IEEE 1800-2023 6.20.1): " << dtypep->prettyNameQ()); @@ -1217,7 +1217,7 @@ class ParamVisitor final : public VNVisitor { } void visit(AstParamTypeDType* nodep) override { iterateChildren(nodep); - if (VN_IS(nodep->subDTypep(), VoidDType)) { + if (VN_IS(nodep->skipRefp(), VoidDType)) { nodep->v3error("Parameter type without default value is never given value" << " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ()); } diff --git a/test_regress/t/t_param_type_bad3.out b/test_regress/t/t_param_type_bad3.out new file mode 100644 index 000000000..f30889150 --- /dev/null +++ b/test_regress/t/t_param_type_bad3.out @@ -0,0 +1,5 @@ +%Error: t/t_param_type_bad3.v:9:26: Expecting a data type: 'PI' + 9 | localparam type P_T = PI; + | ^~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_param_type_bad3.py b/test_regress/t/t_param_type_bad3.py new file mode 100755 index 000000000..45bc705e4 --- /dev/null +++ b/test_regress/t/t_param_type_bad3.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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('vlt') + +test.lint( + # Bug1575 required trace to crash + verilator_flags2=["--trace-vcd"], + fails=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_param_type_bad3.v b/test_regress/t/t_param_type_bad3.v new file mode 100644 index 000000000..3848e7c9e --- /dev/null +++ b/test_regress/t/t_param_type_bad3.v @@ -0,0 +1,10 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + localparam int PI = 6; + localparam type P_T = PI; // Bad +endmodule From 2358f5c2a259c17c4ac56b80961e61cab1d9b538 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 7 May 2025 11:54:18 +0200 Subject: [PATCH 043/211] Fix AstAssignW conversion (#5991) (#5992) --- src/V3Const.cpp | 4 +- test_regress/t/t_math_signed3_noopt.py | 18 ++++ test_regress/t/t_math_signed3_noopt.v | 132 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_math_signed3_noopt.py create mode 100644 test_regress/t/t_math_signed3_noopt.v diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 96fb5b0a7..04ea33435 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3068,8 +3068,8 @@ class ConstVisitor final : public VNVisitor { varrefp->unlinkFrBack(); AstInitial* const newinitp = new AstInitial{ nodep->fileline(), new AstAssign{nodep->fileline(), varrefp, exprp}}; - m_modp->addStmtsp(newinitp); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + nodep->replaceWith(newinitp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); // Set the initial value right in the variable so we can constant propagate AstNode* const initvaluep = exprp->cloneTree(false); varrefp->varp()->valuep(initvaluep); diff --git a/test_regress/t/t_math_signed3_noopt.py b/test_regress/t/t_math_signed3_noopt.py new file mode 100755 index 000000000..929b1beca --- /dev/null +++ b/test_regress/t/t_math_signed3_noopt.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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=["-O0"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_signed3_noopt.v b/test_regress/t/t_math_signed3_noopt.v new file mode 100644 index 000000000..959fb3a6d --- /dev/null +++ b/test_regress/t/t_math_signed3_noopt.v @@ -0,0 +1,132 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2014 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) + +module t (/*AUTOARG*/); + + // verilator lint_off WIDTH + wire [1:0] bug729_au = ~0; + wire signed [1:0] bug729_as = ~0; + wire [2:0] bug729_b = ~0; + // the $signed output is unsigned because the input is unsigned; the signedness does not change. + wire [0:0] bug729_yuu = $signed(2'b11) == 3'b111; //1'b0 + wire [0:0] bug729_ysu = $signed(2'SB11) == 3'b111; //1'b0 + wire [0:0] bug729_yus = $signed(2'b11) == 3'sb111; //1'b1 + wire [0:0] bug729_yss = $signed(2'sb11) == 3'sb111; //1'b1 + wire [0:0] bug729_zuu = 2'sb11 == 3'b111; //1'b0 + wire [0:0] bug729_zsu = 2'sb11 == 3'b111; //1'b0 + wire [0:0] bug729_zus = 2'sb11 == 3'sb111; //1'b1 + wire [0:0] bug729_zss = 2'sb11 == 3'sb111; //1'b1 + + wire [3:0] bug733_a = 4'b0010; + wire [3:0] bug733_yu = $signed(|bug733_a); // 4'b1111 note | is always unsigned + wire signed [3:0] bug733_ys = $signed(|bug733_a); // 4'b1111 + + wire [3:0] bug733_zu = $signed(2'b11); // 4'b1111 + wire signed [3:0] bug733_zs = $signed(2'sb11); // 4'b1111 + + // When RHS of assignment is fewer bits than lhs, RHS sign or zero extends based on RHS's sign + + wire [3:0] bug733_qu = 2'sb11; // 4'b1111 + wire signed [3:0] bug733_qs = 2'sb11; // 4'b1111 + reg signed [32:0] bug349_s; + reg signed [32:0] bug349_u; + + wire signed [1:0] sb11 = 2'sb11; + + wire [3:0] subout_u; + sub sub (.a(2'sb11), .z(subout_u)); + // initial `checkh(subout_u, 4'b1111); + + wire [5:0] cond_a = 1'b1 ? 3'sb111 : 5'sb11111; + initial `checkh(cond_a, 6'b111111); + wire [5:0] cond_b = 1'b0 ? 3'sb111 : 5'sb11111; + initial `checkh(cond_b, 6'b111111); + + bit cmp; + + initial begin +`ifndef VERILATOR + #1; +`endif + + // verilator lint_on WIDTH + `checkh(bug729_yuu, 1'b0); + `checkh(bug729_ysu, 1'b0); + `checkh(bug729_yus, 1'b1); + `checkh(bug729_yss, 1'b1); + + `checkh(bug729_zuu, 1'b0); + `checkh(bug729_zsu, 1'b0); + `checkh(bug729_zus, 1'b1); + `checkh(bug729_zss, 1'b1); + + // `checkh(bug733_yu, 4'b1111); + // `checkh(bug733_ys, 4'b1111); + + `checkh(bug733_zu, 4'b1111); + `checkh(bug733_zs, 4'b1111); + + `checkh(bug733_qu, 4'b1111); + `checkh(bug733_qs, 4'b1111); + + // verilator lint_off WIDTH + bug349_s = 4'sb1111; + `checkh(bug349_s, 33'h1ffffffff); + bug349_u = 4'sb1111; + `checkh(bug349_u, 33'h1ffffffff); + + bug349_s = 4'sb1111 - 1'b1; + `checkh(bug349_s,33'he); + + bug349_s = 4'sb1111 - 5'b00001; + `checkh(bug349_s,33'he); + + cmp = 3'sb111 == 4'b111; + `checkh(cmp, 1); + cmp = 3'sb111 == 4'sb111; + `checkh(cmp, 0); + cmp = 3'sb111 != 4'b111; + `checkh(cmp, 0); + cmp = 3'sb111 != 4'sb111; + `checkh(cmp, 1); + + cmp = 3'sb111 === 4'b111; + `checkh(cmp, 1); + cmp = 3'sb111 === 4'sb111; + `checkh(cmp, 0); + + case (2'sb11) + 4'b1111: $stop; + default: ; + endcase + + case (sb11) + 4'b1111: $stop; + default: ; + endcase + + case (2'sb11) + 4'sb1111: ; + default: $stop; + endcase + + case (sb11) + 4'sb1111: ; + default: $stop; + endcase + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module sub(input [3:0] a, + output [3:0] z); + assign z = a; +endmodule From a80aa07de614504b236934497ebb99dd991256a4 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Wed, 7 May 2025 13:07:16 +0200 Subject: [PATCH 044/211] Support constrained random for associative arrays (#5985) (#5986) --- include/verilated_random.h | 48 ++- include/verilated_types.h | 7 +- src/V3Randomize.cpp | 28 +- test_regress/t/t_constraint_struct_complex.v | 338 ++++++++++++++++++- 4 files changed, 384 insertions(+), 37 deletions(-) diff --git a/include/verilated_random.h b/include/verilated_random.h index aae19ddf1..f75b199d5 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -382,8 +382,9 @@ public: // Register associative array of non-struct types template - void write_var(VlAssocArray& var, int width, const char* name, int dimension, - std::uint32_t randmodeIdx = std::numeric_limits::max()) { + typename std::enable_if::value, void>::type + write_var(VlAssocArray& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { if (m_vars.find(name) != m_vars.end()) return; m_vars[name] = std::make_shared>>( @@ -394,8 +395,13 @@ public: } } - // TODO: Register associative array of structs - + // Register associative array of structs + template + typename std::enable_if::value, void>::type + write_var(VlAssocArray& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); + } // ---------------------------------------- // --- Record Arrays: flat and struct --- // ---------------------------------------- @@ -476,10 +482,12 @@ public: std::vector idxWidths) { std::ostringstream oss; for (size_t i = 0; i < indices.size(); ++i) { - oss << std::hex << static_cast(indices[i]); + oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0') + << static_cast(indices[i]); if (i < indices.size() - 1) oss << "."; } - write_var(var, 1ULL, (name + "." + oss.str()).c_str(), 1ULL); + write_var(var, 1ULL, + oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL); } // Recursively process VlUnpacked of structs @@ -487,7 +495,8 @@ public: void record_struct_arr(VlUnpacked& var, const std::string name, int dimension, std::vector indices, std::vector idxWidths) { if (dimension > 0 && N_Depth != 0) { - idxWidths.push_back(32); + constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1); + idxWidths.push_back(idx_width); for (size_t i = 0; i < N_Depth; ++i) { indices.push_back(i); record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths); @@ -510,9 +519,32 @@ public: } } - // TODO: Add support for associative arrays of structs // Recursively process associative arrays of structs + template + void record_struct_arr(VlAssocArray& var, const std::string name, + int dimension, std::vector indices, + std::vector idxWidths) { + if ((dimension > 0) && (!var.empty())) { + for (auto it = var.begin(); it != var.end(); ++it) { + const T_Key& key = it->first; + const T_Value& value = it->second; + std::string indexed_name; + std::vector integral_index; + size_t idx_width = 0; + + process_key(key, indexed_name, integral_index, name, idx_width); + std::ostringstream oss; + for (int i = 0; i < integral_index.size(); ++i) + oss << std::hex << static_cast(integral_index[i]); + + std::string result = oss.str(); + result.insert(result.begin(), int(idx_width / 4) - result.size(), '0'); + record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices, + idxWidths); + } + } + } // -------------------------- // --- Helper functions --- // -------------------------- diff --git a/include/verilated_types.h b/include/verilated_types.h index 783ba5a65..70e131423 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -969,6 +969,7 @@ public: // Size of array. Verilog: function int size(), or int num() int size() const { return m_map.size(); } + bool empty() const { return m_map.empty(); } // Clear array. Verilog: function void delete([input index]) void clear() { m_map.clear(); } void erase(const T_Key& index) { m_map.erase(index); } @@ -1265,7 +1266,7 @@ std::string VL_TO_STRING(const VlAssocArray& obj) { } template -struct VlContainsCustomStruct> : VlContainsCustomStruct {}; +struct VlContainsCustomStruct> : VlContainsCustomStruct {}; template void VL_READMEM_N(bool hex, int bits, const std::string& filename, @@ -1597,8 +1598,8 @@ std::string VL_TO_STRING(const VlUnpacked& obj) { return obj.to_string(); } -template -struct VlContainsCustomStruct> : VlContainsCustomStruct {}; +template +struct VlContainsCustomStruct> : VlContainsCustomStruct {}; //=================================================================== // Helper to apply the given indices to a target expression diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index ff7353eb9..c1e5c756d 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -541,7 +541,7 @@ class ConstraintExprVisitor final : public VNVisitor { AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp}; if (m_structSel && newp->name() == "(select %@ %@)") { newp->name("%@.%@"); - newp->exprsp()->nextp()->name("%x"); + if (!VN_IS(nodep, AssocSel)) newp->exprsp()->nextp()->name("%x"); } nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -734,8 +734,10 @@ class ConstraintExprVisitor final : public VNVisitor { } } // Mark Random for structArray - if (VN_IS(nodep->fromp(), ArraySel)) { - AstNodeExpr* const fromp = VN_AS(nodep->fromp(), ArraySel)->fromp(); + if (VN_IS(nodep->fromp(), ArraySel) || VN_IS(nodep->fromp(), CMethodHard)) { + AstNodeExpr* const fromp = VN_IS(nodep->fromp(), ArraySel) + ? VN_AS(nodep->fromp(), ArraySel)->fromp() + : VN_AS(nodep->fromp(), CMethodHard)->fromp(); AstStructDType* const dtypep = VN_AS(fromp->dtypep()->skipRefp()->subDTypep()->skipRefp(), StructDType); dtypep->markConstrainedRand(true); @@ -755,7 +757,8 @@ class ConstraintExprVisitor final : public VNVisitor { if (VN_AS(nodep->fromp(), SFormatF)->name() == "%@.%@") { newp = new AstSFormatF{fl, "%@.%@." + nodep->name(), false, VN_AS(nodep->fromp(), SFormatF)->exprsp()->cloneTreePure(true)}; - newp->exprsp()->nextp()->name("%x"); + if (newp->exprsp()->nextp()->name().rfind("#x", 0) == 0) + newp->exprsp()->nextp()->name("%x"); // for #x%x to %x } else { newp = new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, nullptr}; @@ -767,10 +770,11 @@ class ConstraintExprVisitor final : public VNVisitor { void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); + // Adaptive formatting and type handling for associative array keys if (VN_IS(nodep->bitp(), VarRef) && VN_AS(nodep->bitp(), VarRef)->isString()) { VNRelinker handle; - AstNodeExpr* const idxp - = new AstSFormatF{fl, "#x%32p", false, nodep->bitp()->unlinkFrBack(&handle)}; + AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32p" : "#x%32p"), false, + nodep->bitp()->unlinkFrBack(&handle)}; handle.relink(idxp); editSMT(nodep, nodep->fromp(), idxp); } else if (VN_IS(nodep->bitp(), CvtPackString) @@ -784,8 +788,8 @@ class ConstraintExprVisitor final : public VNVisitor { << stringSize << "bits, limit is 128 bits"); } VNRelinker handle; - AstNodeExpr* const idxp - = new AstSFormatF{fl, "#x%32x", false, stringp->lhsp()->unlinkFrBack(&handle)}; + AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32x" : "#x%32x"), false, + stringp->lhsp()->unlinkFrBack(&handle)}; handle.relink(idxp); editSMT(nodep, nodep->fromp(), idxp); } else { @@ -799,13 +803,13 @@ class ConstraintExprVisitor final : public VNVisitor { std::string fmt; // Normalize to standard bit width if (actual_width <= 8) { - fmt = "#x%2x"; + fmt = m_structSel ? "%2x" : "#x%2x"; } else if (actual_width <= 16) { - fmt = "#x%4x"; + fmt = m_structSel ? "%4x" : "#x%4x"; } else { - fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; + fmt = (m_structSel ? "%" : "#x%") + + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; } - AstNodeExpr* const idxp = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; handle.relink(idxp); diff --git a/test_regress/t/t_constraint_struct_complex.v b/test_regress/t/t_constraint_struct_complex.v index 30626219c..6ad6ba90e 100755 --- a/test_regress/t/t_constraint_struct_complex.v +++ b/test_regress/t/t_constraint_struct_complex.v @@ -4,8 +4,10 @@ // any use, without warranty, 2025 by PlanV GmbH. // SPDX-License-Identifier: CC0-1.0 + class ArrayStruct; /* verilator lint_off SIDEEFFECT */ + // Struct with an unpacked array typedef int arr_3_t[3]; typedef int arr_4_t[4]; @@ -51,19 +53,25 @@ class ArrayStruct; foreach (s1.arr[i]) s1.arr[i] inside {1, 2, 3, 4}; foreach (s1.arr_3[i]) s1.arr_3[i] inside {11, 22, 33, 44, 55}; } + constraint c_dynamic { foreach (s2.arr[i]) s2.arr[i] inside {[10:20]}; } + constraint c_queue { foreach (s3.arr[i]) s3.arr[i] inside {[100:200]}; } + constraint c_assoc { s4.arr["one"] inside {[10:50]}; s4.arr["two"] inside {[51:100]}; s4.arr["three"] inside {[101:150]}; } + constraint c_multi_dim { foreach (s5.arr[i, j]) s5.arr[i][j] inside {[0:9]}; } + constraint c_mix { foreach (s6.mix_arr[i, j]) s6.mix_arr[i][j] inside {[50:100]}; } function new(); + s1.arr = '{1, 2, 3}; s1.arr_3 = '{1, 2, 3}; s1.arr_4 = '{0, 2, 3, 4}; @@ -81,9 +89,11 @@ class ArrayStruct; foreach (s6.mix_arr[i]) begin s6.mix_arr[i] = new[i + 1]; end + endfunction function void print(); + foreach (s1.arr[i]) $display("s1.arr[%0d] = %0d", i, s1.arr[i]); foreach (s1.arr_3[i]) $display("s1.arr_3[%0d] = %0d", i, s1.arr_3[i]); foreach (s1.arr_4[i]) $display("s1.arr_4[%0d] = %0d", i, s1.arr_4[i]); @@ -92,10 +102,12 @@ class ArrayStruct; foreach (s4.arr[i]) $display("s4.arr[\"%s\"] = %0d", i, s4.arr[i]); foreach (s5.arr[i, j]) $display("s5.arr[%0d][%0d] = %0d", i, j, s5.arr[i][j]); foreach (s6.mix_arr[i, j]) $display("s6.mix_arr[%0d][%0d] = %0d", i, j, s6.mix_arr[i][j]); + endfunction // Self-test function to verify constraints function void self_test(); + foreach (s1.arr[i]) if (!(s1.arr[i] inside {1, 2, 3, 4})) $stop; foreach (s1.arr_3[i]) if (!(s1.arr_3[i] inside {11, 22, 33, 44, 55})) $stop; // Note: s1.arr_4[0] is not rand @@ -108,6 +120,7 @@ class ArrayStruct; foreach (s5.arr[i, j]) if (!(s5.arr[i][j] inside {[0:9]})) $stop; foreach (s6.mix_arr[i]) if (s6.mix_arr[i].size() == 0) $stop; foreach (s6.mix_arr[i, j]) if (!(s6.mix_arr[i][j] inside {[50:100]})) $stop; + endfunction /* verilator lint_off SIDEEFFECT */ endclass @@ -115,66 +128,363 @@ endclass class StructArray; /* verilator lint_off WIDTHTRUNC */ typedef struct { - rand int arr[3]; + rand int arr[3]; // static unpacked array rand int a; rand bit [3:0] b; bit c; } struct_t; - rand struct_t s_arr[2]; - constraint c_structArray_0 { + rand struct_t s_arr[2]; + rand struct_t s_2d_arr[2][3]; + rand struct_t s_dyn_arr[]; + rand struct_t s_que_arr[$]; + rand struct_t s_assoc_arr[string]; + rand struct_t s_assoc_arr_2[bit[5:0]]; + + constraint c_arr { foreach (s_arr[i]) foreach (s_arr[i].arr[j]) s_arr[i].arr[j] inside {[0:9]}; + foreach (s_2d_arr[i, j]) + foreach (s_2d_arr[i][j].arr[k]) + s_2d_arr[i][j].arr[k] inside {[9:19]}; + foreach (s_dyn_arr[i]) + foreach (s_dyn_arr[i].arr[j]) + s_dyn_arr[i].arr[j] inside {[19:29]}; + foreach (s_que_arr[i]) + foreach (s_que_arr[i].arr[j]) + s_que_arr[i].arr[j] inside {[29:39]}; + foreach (s_assoc_arr[i]) + foreach (s_assoc_arr[i].arr[j]) + s_assoc_arr[i].arr[j] inside {[39:49]}; + foreach (s_assoc_arr_2[i]) + foreach (s_assoc_arr_2[i].arr[j]) + s_assoc_arr_2[i].arr[j] inside {[49:59]}; } - constraint c_structArray_1 { - foreach (s_arr[i]) s_arr[i].a inside {[10:20]}; + + constraint c_others { + foreach (s_arr[i]) s_arr[i].a inside {[40:50]}; + foreach (s_arr[i]) s_arr[i].b inside {[0:7]}; + + foreach (s_2d_arr[i, j]) s_2d_arr[i][j].a inside {[50:60]}; + + foreach (s_dyn_arr[i]) s_dyn_arr[i].a inside {[60:70]}; + + foreach (s_que_arr[i]) s_que_arr[i].a inside {[70:80]}; + + foreach (s_assoc_arr[i]) s_assoc_arr[i].a inside {[80:90]}; + + foreach (s_assoc_arr_2[i]) s_assoc_arr_2[i].a inside {[90:100]}; } function new(); + foreach (s_arr[i]) begin - foreach (s_arr[i].arr[j]) s_arr[i].arr[j] = 'h0 + j; - s_arr[i].a = 'h10 + i; - s_arr[i].b = 'h0 + i; - s_arr[i].c = i; + foreach (s_arr[i].arr[j]) + s_arr[i].arr[j] = j; + s_arr[i].a = 40 + i; + s_arr[i].b = i; + s_arr[i].c = 0; end + + foreach (s_2d_arr[i, j]) begin + foreach (s_2d_arr[i][j].arr[k]) + s_2d_arr[i][j].arr[k] = k + 10; + s_2d_arr[i][j].a = 50 + i + j; + s_2d_arr[i][j].b = i + j; + s_2d_arr[i][j].c = 0; + end + + foreach (s_dyn_arr[i]) begin + s_dyn_arr = new[3]; + foreach (s_dyn_arr[i].arr[j]) + s_dyn_arr[i].arr[j] = j + 20; + s_dyn_arr[i].a = 60 + i; + s_dyn_arr[i].b = i; + s_dyn_arr[i].c = 0; + end + + for (int i = 0; i < 3; i++) begin + s_que_arr.push_back('{arr: '{30, 31, 32}, a: 70 + i, b: i, c: 0}); + end + + // Associative array with string index + foreach (s_assoc_arr["x"].arr[j]) + s_assoc_arr["x"].arr[j] = j + 40; + foreach (s_assoc_arr["y"].arr[j]) + s_assoc_arr["y"].arr[j] = j + 50; + foreach (s_assoc_arr["long_string_index"].arr[j]) + s_assoc_arr["long_string_index"].arr[j] = j + 60; + s_assoc_arr["x"].a = 80; + s_assoc_arr["x"].b = 0; + s_assoc_arr["x"].c = 0; + s_assoc_arr["y"].a = 90; + s_assoc_arr["y"].b = 1; + s_assoc_arr["y"].c = 0; + s_assoc_arr["long_string_index"].a = 100; + s_assoc_arr["long_string_index"].b = 2; + s_assoc_arr["long_string_index"].c = 0; + + foreach (s_assoc_arr_2[6'd30].arr[j]) + s_assoc_arr_2[6'd30].arr[j] = j + 70; + foreach (s_assoc_arr_2[6'd7].arr[j]) + s_assoc_arr_2[6'd7].arr[j] = j + 80; + s_assoc_arr_2[6'd30].a = 90; + s_assoc_arr_2[6'd30].b = 0; + s_assoc_arr_2[6'd30].c = 0; + s_assoc_arr_2[6'd7].a = 100; + s_assoc_arr_2[6'd7].b = 1; + s_assoc_arr_2[6'd7].c = 0; + endfunction function void print(); + foreach (s_arr[i]) begin - foreach (s_arr[i].arr[j]) $display("s_arr[%0d].arr[%0d] = %0d", i, j, s_arr[i].arr[j]); + foreach (s_arr[i].arr[j]) + $display("s_arr[%0d].arr[%0d] = %0d", i, j, s_arr[i].arr[j]); $display("s_arr[%0d].a = %0d", i, s_arr[i].a); $display("s_arr[%0d].b = %0d", i, s_arr[i].b); $display("s_arr[%0d].c = %0d", i, s_arr[i].c); end + + foreach (s_2d_arr[i, j]) begin + foreach (s_2d_arr[i][j].arr[k]) + $display("s_2d_arr[%0d][%0d].arr[%0d] = %0d", i, j, k, s_2d_arr[i][j].arr[k]); + $display("s_2d_arr[%0d][%0d].a = %0d", i, j, s_2d_arr[i][j].a); + $display("s_2d_arr[%0d][%0d].b = %0d", i, j, s_2d_arr[i][j].b); + $display("s_2d_arr[%0d][%0d].c = %0d", i, j, s_2d_arr[i][j].c); + end + + foreach (s_dyn_arr[i]) begin + foreach (s_dyn_arr[i].arr[j]) + $display("s_dyn_arr[%0d].arr[%0d] = %0d", i, j, s_dyn_arr[i].arr[j]); + $display("s_dyn_arr[%0d].a = %0d", i, s_dyn_arr[i].a); + $display("s_dyn_arr[%0d].b = %0d", i, s_dyn_arr[i].b); + $display("s_dyn_arr[%0d].c = %0d", i, s_dyn_arr[i].c); + end + + foreach (s_que_arr[i]) begin + foreach (s_que_arr[i].arr[j]) + $display("s_que_arr[%0d].arr[%0d] = %0d", i, j, s_que_arr[i].arr[j]); + $display("s_que_arr[%0d].a = %0d", i, s_que_arr[i].a); + $display("s_que_arr[%0d].b = %0d", i, s_que_arr[i].b); + $display("s_que_arr[%0d].c = %0d", i, s_que_arr[i].c); + end + + foreach (s_assoc_arr["x"].arr[j]) + $display("s_assoc_arr[x].arr[%0d] = %0d", j, s_assoc_arr["x"].arr[j]); + $display("s_assoc_arr[x].a = %0d", s_assoc_arr["x"].a); + $display("s_assoc_arr[x].b = %0d", s_assoc_arr["x"].b); + $display("s_assoc_arr[x].c = %0d", s_assoc_arr["x"].c); + foreach (s_assoc_arr["y"].arr[j]) + $display("s_assoc_arr[y].arr[%0d] = %0d", j, s_assoc_arr["y"].arr[j]); + $display("s_assoc_arr[y].a = %0d", s_assoc_arr["y"].a); + $display("s_assoc_arr[y].b = %0d", s_assoc_arr["y"].b); + $display("s_assoc_arr[y].c = %0d", s_assoc_arr["y"].c); + foreach (s_assoc_arr["long_string_index"].arr[j]) + $display("s_assoc_arr[long_string_index].arr[%0d] = %0d", j, s_assoc_arr["long_string_index"].arr[j]); + $display("s_assoc_arr[long_string_index].a = %0d", s_assoc_arr["long_string_index"].a); + $display("s_assoc_arr[long_string_index].b = %0d", s_assoc_arr["long_string_index"].b); + $display("s_assoc_arr[long_string_index].c = %0d", s_assoc_arr["long_string_index"].c); + + foreach (s_assoc_arr_2[6'd30].arr[j]) + $display("s_assoc_arr_2[30].arr[%0d] = %0d", j, s_assoc_arr_2[6'd30].arr[j]); + $display("s_assoc_arr_2[30].a = %0d", s_assoc_arr_2[6'd30].a); + $display("s_assoc_arr_2[30].b = %0d", s_assoc_arr_2[6'd30].b); + $display("s_assoc_arr_2[30].c = %0d", s_assoc_arr_2[6'd30].c); + foreach (s_assoc_arr_2[6'd7].arr[j]) + $display("s_assoc_arr_2[7].arr[%0d] = %0d", j, s_assoc_arr_2[6'd7].arr[j]); + $display("s_assoc_arr_2[7].a = %0d", s_assoc_arr_2[6'd7].a); + $display("s_assoc_arr_2[7].b = %0d", s_assoc_arr_2[6'd7].b); + $display("s_assoc_arr_2[7].c = %0d", s_assoc_arr_2[6'd7].c); + endfunction function void self_test(); + foreach (s_arr[i]) begin - foreach (s_arr[i].arr[j]) if (!(s_arr[i].arr[j] inside {[0:9]})) $stop; - if (!(s_arr[i].a inside {[10:20]})) $stop; - if (!(s_arr[0].c == 0)) $stop; - if (!(s_arr[1].c == 1)) $stop; + foreach (s_arr[i].arr[j]) + if (!(s_arr[i].arr[j] inside {[0:9]})) $stop; + if (!(s_arr[i].a inside {[40:50]})) $stop; end + + foreach (s_2d_arr[i, j]) begin + foreach (s_2d_arr[i][j].arr[k]) + if (!(s_2d_arr[i][j].arr[k] inside {[9:19]})) $stop; + if (!(s_2d_arr[i][j].a inside {[50:60]})) $stop; + end + + foreach (s_dyn_arr[i]) begin + foreach (s_dyn_arr[i].arr[j]) + if (!(s_dyn_arr[i].arr[j] inside {[19:29]})) $stop; + if (!(s_dyn_arr[i].a inside {[60:70]})) $stop; + end + + foreach (s_que_arr[i]) begin + foreach (s_que_arr[i].arr[j]) + if (!(s_que_arr[i].arr[j] inside {[29:39]})) $stop; + if (!(s_que_arr[i].a inside {[70:80]})) $stop; + end + + foreach (s_assoc_arr["x"].arr[j]) + if (!(s_assoc_arr["x"].arr[j] inside {[39:49]})) $stop; + if (!(s_assoc_arr["x"].a inside {[80:90]})) $stop; + foreach (s_assoc_arr["y"].arr[j]) + if (!(s_assoc_arr["y"].arr[j] inside {[39:49]})) $stop; + if (!(s_assoc_arr["y"].a inside {[80:90]})) $stop; + foreach (s_assoc_arr["long_string_index"].arr[j]) + if (!(s_assoc_arr["long_string_index"].arr[j] inside {[39:49]})) $stop; + if (!(s_assoc_arr["long_string_index"].a inside {[80:90]})) $stop; + + foreach (s_assoc_arr_2[6'd30].arr[j]) + if (!(s_assoc_arr_2[6'd30].arr[j] inside {[49:59]})) $stop; + if (!(s_assoc_arr_2[6'd30].a inside {[90:100]})) $stop; + foreach (s_assoc_arr_2[6'd7].arr[j]) + if (!(s_assoc_arr_2[6'd7].arr[j] inside {[49:59]})) $stop; + if (!(s_assoc_arr_2[6'd7].a inside {[90:100]})) $stop; + endfunction + + /* verilator lint_off WIDTHTRUNC */ +endclass + +class MixedStructure; + /* verilator lint_off WIDTHTRUNC */ + typedef struct { + rand int arr[3]; // static unpacked array + rand int dyn[]; // dynamic array + rand int que[$]; // queue + rand int assoc[string]; // associative array with string key + rand int a; + rand bit [3:0] b; + bit c; + } struct_t; + + rand struct_t s_arr[2]; + + constraint c_static { + foreach (s_arr[i]) + foreach (s_arr[i].arr[j]) + s_arr[i].arr[j] inside {[0:9]}; + } + + constraint c_dyn { + foreach (s_arr[i]) + foreach (s_arr[i].dyn[j]) + s_arr[i].dyn[j] inside {[10:19]}; + } + + constraint c_queue { + foreach (s_arr[i]) + foreach (s_arr[i].que[j]) + s_arr[i].que[j] inside {[20:29]}; + } + + constraint c_assoc { + foreach (s_arr[i]) { + s_arr[i].assoc["x"] inside {[30:39]}; + s_arr[i].assoc["y"] inside {[30:39]}; + } + } + + constraint c_other { + foreach (s_arr[i]) s_arr[i].a inside {[40:50]}; + } + + function new(); + + foreach (s_arr[i]) begin + s_arr[i].dyn = new[2]; + s_arr[i].que = {0, 0}; + s_arr[i].assoc = '{"x": 0, "y": 0}; + + foreach (s_arr[i].arr[j]) + s_arr[i].arr[j] = j; + foreach (s_arr[i].dyn[j]) + s_arr[i].dyn[j] = 10 + j; + foreach (s_arr[i].que[j]) + s_arr[i].que[j] = 20 + j; + + s_arr[i].assoc["x"] = i + 30; + s_arr[i].assoc["y"] = i + 31; + s_arr[i].a = 40 + i; + s_arr[i].b = i; + s_arr[i].c = i; + end + + endfunction + + function void print(); + + foreach (s_arr[i]) begin + foreach (s_arr[i].arr[j]) + $display("s_arr[%0d].arr[%0d] = %0d", i, j, s_arr[i].arr[j]); + foreach (s_arr[i].dyn[j]) + $display("s_arr[%0d].dyn[%0d] = %0d", i, j, s_arr[i].dyn[j]); + foreach (s_arr[i].que[j]) + $display("s_arr[%0d].que[%0d] = %0d", i, j, s_arr[i].que[j]); + + $display("s_arr[%0d].assoc[\"x\"] = %0d", i, s_arr[i].assoc["x"]); + $display("s_arr[%0d].assoc[\"y\"] = %0d", i, s_arr[i].assoc["y"]); + $display("s_arr[%0d].a = %0d", i, s_arr[i].a); + $display("s_arr[%0d].b = %0d", i, s_arr[i].b); + $display("s_arr[%0d].c = %0d", i, s_arr[i].c); + end + + endfunction + + function void self_test(); + + foreach (s_arr[i]) begin + foreach (s_arr[i].arr[j]) + if (!(s_arr[i].arr[j] inside {[0:9]})) $stop; + foreach (s_arr[i].dyn[j]) + if (!(s_arr[i].dyn[j] inside {[10:19]})) $stop; + foreach (s_arr[i].que[j]) + if (!(s_arr[i].que[j] inside {[20:29]})) $stop; + if (!(s_arr[i].assoc.exists("x") && s_arr[i].assoc["x"] inside {[30:39]})) $stop; + if (!(s_arr[i].assoc.exists("y") && s_arr[i].assoc["y"] inside {[30:39]})) $stop; + if (!(s_arr[i].a inside {[40:50]})) $stop; + if (i == 0 && s_arr[i].c != 0) $stop; + if (i == 1 && s_arr[i].c != 1) $stop; + end + + endfunction + /* verilator lint_off WIDTHTRUNC */ endclass module t_constraint_struct_complex; + int success; ArrayStruct as_c; StructArray sa_c; + MixedStructure mixed_c; + initial begin as_c = new(); sa_c = new(); + mixed_c = new(); + success = as_c.randomize(); if (success != 1) $stop; as_c.self_test(); // as_c.print(); + // $display(" ArrayStruct passed! \n"); + success = sa_c.randomize(); if (success != 1) $stop; sa_c.self_test(); // sa_c.print(); + // $display(" StructArray passed! \n"); + + success = mixed_c.randomize(); + if (success != 1) $stop; + mixed_c.self_test(); + // mixed_c.print(); + // $display(" MixedStructure passed! \n"); + $write("*-* All Finished *-*\n"); $finish; end From 5f4646f617ac81c7633f4f07d0c852ecb0ea485f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Thu, 8 May 2025 12:45:10 +0200 Subject: [PATCH 045/211] Ignore dependencies from different hierarchical schedules (#5954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bartłomiej Chmiel --- src/V3ExecGraph.cpp | 167 ++++++++++++++++++++++------ test_regress/t/t_hier_block_perf.py | 2 +- 2 files changed, 133 insertions(+), 36 deletions(-) diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 4052f7836..7d1d466d7 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -66,6 +66,7 @@ class ThreadSchedule final { uint32_t m_id; // Unique ID of a schedule static uint32_t s_nextId; // Next ID number to use std::unordered_set mtasks; // Mtasks in this schedule + uint32_t m_endTime = 0; // Latest task end time in this schedule public: // CONSTANTS @@ -196,6 +197,7 @@ public: uint32_t scheduleOn(const ExecMTask* mtaskp, uint32_t bestThreadId) { mtasks.emplace(mtaskp); const uint32_t bestEndTime = mtaskp->predictStart() + mtaskp->cost(); + m_endTime = std::max(m_endTime, bestEndTime); mtaskState[mtaskp].completionTime = bestEndTime; mtaskState[mtaskp].threadId = bestThreadId; @@ -208,6 +210,7 @@ public: return bestEndTime; } bool contains(const ExecMTask* mtaskp) const { return mtasks.count(mtaskp); } + uint32_t endTime() const { return m_endTime; } }; uint32_t ThreadSchedule::s_nextId = 0; @@ -256,6 +259,8 @@ class PackThreads final { // METHODS uint32_t completionTime(const ThreadSchedule& schedule, const ExecMTask* mtaskp, uint32_t threadId) { + // Ignore tasks that were scheduled on a different schedule + if (!schedule.contains(mtaskp)) return 0; const ThreadSchedule::MTaskState& state = schedule.mtaskState.at(mtaskp); UASSERT(state.threadId != ThreadSchedule::UNASSIGNED, "Mtask should have assigned thread"); if (threadId == state.threadId) { @@ -373,19 +378,24 @@ class PackThreads final { } } + const uint32_t endTime = schedule.endTime(); + if (!bestMtaskp && mode == SchedulingMode::WIDE_TASK_DISCOVERED) { mode = SchedulingMode::WIDE_TASK_SCHEDULING; const uint32_t size = m_nThreads / maxThreadWorkers; UASSERT(size, "Thread pool size should be bigger than 0"); - // If no tasks were added to the normal thread schedule, remove it. - if (schedule.mtaskState.empty()) result.erase(result.begin()); + // If no tasks were added to the normal thread schedule, clear it. + if (schedule.mtaskState.empty()) result.clear(); result.emplace_back(ThreadSchedule{size}); + std::fill(busyUntil.begin(), busyUntil.end(), endTime); continue; } if (!bestMtaskp && mode == SchedulingMode::WIDE_TASK_SCHEDULING) { mode = SchedulingMode::SCHEDULING; - if (!schedule.mtaskState.empty()) result.emplace_back(ThreadSchedule{m_nThreads}); + UASSERT(!schedule.mtaskState.empty(), "Mtask should be added"); + result.emplace_back(ThreadSchedule{m_nThreads}); + std::fill(busyUntil.begin(), busyUntil.end(), endTime); continue; } @@ -393,24 +403,7 @@ class PackThreads final { bestMtaskp->predictStart(bestTime); const uint32_t bestEndTime = schedule.scheduleOn(bestMtaskp, bestThreadId); - - // Populate busyUntil timestamps. For multi-worker tasks, set timestamps for - // offsetted threads. - if (mode != SchedulingMode::WIDE_TASK_SCHEDULING) { - busyUntil[bestThreadId] = bestEndTime; - } else { - for (int i = 0; i < maxThreadWorkers; ++i) { - const size_t threadId = bestThreadId + (i * schedule.threads.size()); - UASSERT(threadId < busyUntil.size(), - "Incorrect busyUntil offset: threadId=" + cvtToStr(threadId) - + " bestThreadId=" + cvtToStr(bestThreadId) + " i=" + cvtToStr(i) - + " schedule-size=" + cvtToStr(schedule.threads.size()) - + " maxThreadWorkers=" + cvtToStr(maxThreadWorkers)); - busyUntil[threadId] = bestEndTime; - UINFO(6, "Will schedule " << bestMtaskp->name() << " onto thread " << threadId - << endl); - } - } + busyUntil[bestThreadId] = bestEndTime; // Update the ready list const size_t erased = readyMTasks.erase(bestMtaskp); @@ -439,6 +432,10 @@ class PackThreads final { public: // SELF TEST static void selfTest() { + selfTestHierFirst(); + selfTestNormalFirst(); + } + static void selfTestNormalFirst() { V3Graph graph; FileLine* const flp = v3Global.rootp()->fileline(); std::vector mTaskBodyps; @@ -466,18 +463,28 @@ public: t4->cost(100); t4->priority(100); t4->threads(3); + ExecMTask* const t5 = new ExecMTask{&graph, makeBody()}; + t5->cost(100); + t5->priority(100); + ExecMTask* const t6 = new ExecMTask{&graph, makeBody()}; + t6->cost(100); + t6->priority(100); /* 0 / \ 1 2 / \ - 3 4 + 3 4 + / \ + 5 6 */ new V3GraphEdge{&graph, t0, t1, 1}; new V3GraphEdge{&graph, t0, t2, 1}; new V3GraphEdge{&graph, t2, t3, 1}; new V3GraphEdge{&graph, t2, t4, 1}; + new V3GraphEdge{&graph, t3, t5, 1}; + new V3GraphEdge{&graph, t4, t6, 1}; constexpr uint32_t threads = 6; PackThreads packer{threads, @@ -485,6 +492,7 @@ public: 10}; // Sandbag denom const std::vector scheduled = packer.pack(graph); + UASSERT_SELFTEST(size_t, scheduled.size(), 3); UASSERT_SELFTEST(size_t, scheduled[0].threads.size(), threads); UASSERT_SELFTEST(size_t, scheduled[0].threads[0].size(), 2); for (size_t i = 1; i < scheduled[0].threads.size(); ++i) @@ -494,17 +502,23 @@ public: UASSERT_SELFTEST(const ExecMTask*, scheduled[0].threads[0][1], t1); UASSERT_SELFTEST(size_t, scheduled[1].threads.size(), threads / 3); - UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[1][0], t2); - UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[1][1], t3); - UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[0][0], t4); + UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[0][0], t2); + UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[0][1], t3); + UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[1][0], t4); - UASSERT_SELFTEST(size_t, ThreadSchedule::mtaskState.size(), 5); + UASSERT_SELFTEST(size_t, scheduled[2].threads.size(), threads); + UASSERT_SELFTEST(const ExecMTask*, scheduled[2].threads[0][0], t5); + UASSERT_SELFTEST(const ExecMTask*, scheduled[2].threads[1][0], t6); + + UASSERT_SELFTEST(size_t, ThreadSchedule::mtaskState.size(), 7); UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t0), 0); UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t1), 0); - UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t2), 1); - UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t3), 1); - UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t4), 0); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t2), 0); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t3), 0); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t4), 1); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t5), 0); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t6), 1); // On its native thread, we see the actual end time for t0: UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[0], t0, 0), 1000); @@ -518,14 +532,97 @@ public: // with t0's sandbagged time; compounding caused trouble in // practice. UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[0], t1, 1), 1130); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 0), 1229); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 1), 1199); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 0), 1329); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 1), 1299); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 0), 1329); - UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 1), 1359); + + // Wide task scheduling + + // Task does not depend on previous or future schedules + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[0], t2, 0), 0); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[2], t2, 0), 0); + + // We allow sandbagging for hierarchical children tasks, this does not affect + // wide task scheduling. When the next schedule is created it doesn't matter + // anyway. + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 0), 1200); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 1), 1230); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 2), 1230); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 3), 1230); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 4), 1230); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t2, 5), 1230); + + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 0), 1300); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 1), 1330); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 2), 1330); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 3), 1330); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 4), 1330); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t3, 5), 1330); + + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 0), 1360); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 1), 1330); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 2), 1360); + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[1], t4, 3), 1360); + 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(); + ThreadSchedule::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()}; + t0->cost(1000); + t0->priority(1100); + t0->threads(2); + ExecMTask* const t1 = new ExecMTask{&graph, makeBody()}; + t1->cost(100); + t1->priority(100); + + /* + 0 + | + 1 + */ + new V3GraphEdge{&graph, t0, t1, 1}; + + constexpr uint32_t threads = 2; + PackThreads packer{threads, + 3, // Sandbag numerator + 10}; // Sandbag denom + + const std::vector scheduled = packer.pack(graph); + UASSERT_SELFTEST(size_t, scheduled.size(), 2); + UASSERT_SELFTEST(size_t, scheduled[0].threads.size(), threads / 2); + UASSERT_SELFTEST(size_t, scheduled[0].threads[0].size(), 1); + for (size_t i = 1; i < scheduled[0].threads.size(); ++i) + UASSERT_SELFTEST(size_t, scheduled[0].threads[i].size(), 0); + + UASSERT_SELFTEST(const ExecMTask*, scheduled[0].threads[0][0], t0); + + UASSERT_SELFTEST(size_t, scheduled[1].threads.size(), threads); + UASSERT_SELFTEST(size_t, scheduled[1].threads[0].size(), 1); + for (size_t i = 1; i < scheduled[1].threads.size(); ++i) + UASSERT_SELFTEST(size_t, scheduled[1].threads[i].size(), 0); + UASSERT_SELFTEST(const ExecMTask*, scheduled[1].threads[0][0], t1); + + UASSERT_SELFTEST(size_t, ThreadSchedule::mtaskState.size(), 2); + + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t0), 0); + UASSERT_SELFTEST(uint32_t, ThreadSchedule::threadId(t1), 0); + + UASSERT_SELFTEST(uint32_t, packer.completionTime(scheduled[0], t0, 0), 1000); + + 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(); + ThreadSchedule::mtaskState.clear(); } static std::vector apply(V3Graph& mtaskGraph) { diff --git a/test_regress/t/t_hier_block_perf.py b/test_regress/t/t_hier_block_perf.py index 5e3ddef08..b36132b41 100755 --- a/test_regress/t/t_hier_block_perf.py +++ b/test_regress/t/t_hier_block_perf.py @@ -36,7 +36,7 @@ if test.vltmt: test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", r'Optimizations, Thread schedule count\s+(\d+)', 4) test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", - r'Optimizations, Thread schedule total tasks\s+(\d+)', 10) + r'Optimizations, Thread schedule total tasks\s+(\d+)', 12) test.execute(all_run_flags=[ "+verilator+prof+exec+start+2", From c2d289dc71835b42283c5e32a13362cf16dfeab3 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 9 May 2025 17:09:33 +0200 Subject: [PATCH 046/211] Tests: Fix t_math_signed3 test (#5995) --- test_regress/t/t_math_signed3.py | 2 +- test_regress/t/t_math_signed3.v | 18 +++- test_regress/t/t_math_signed3_noopt.py | 4 +- test_regress/t/t_math_signed3_noopt.v | 132 ------------------------- 4 files changed, 17 insertions(+), 139 deletions(-) delete mode 100644 test_regress/t/t_math_signed3_noopt.v diff --git a/test_regress/t/t_math_signed3.py b/test_regress/t/t_math_signed3.py index d4f986441..43eddedd0 100755 --- a/test_regress/t/t_math_signed3.py +++ b/test_regress/t/t_math_signed3.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(make_main=False, verilator_flags2=["--main", "--exe", "--timing"]) test.execute() diff --git a/test_regress/t/t_math_signed3.v b/test_regress/t/t_math_signed3.v index 24970ac5e..ffb2a358e 100644 --- a/test_regress/t/t_math_signed3.v +++ b/test_regress/t/t_math_signed3.v @@ -41,19 +41,27 @@ module t (/*AUTOARG*/); wire [3:0] subout_u; sub sub (.a(2'sb11), .z(subout_u)); - initial `checkh(subout_u, 4'b1111); + initial begin + #1; + `checkh(subout_u, 4'b1111); + end wire [5:0] cond_a = 1'b1 ? 3'sb111 : 5'sb11111; - initial `checkh(cond_a, 6'b111111); + initial begin + #1; + `checkh(cond_a, 6'b111111); + end + wire [5:0] cond_b = 1'b0 ? 3'sb111 : 5'sb11111; - initial `checkh(cond_b, 6'b111111); + initial begin + #1; + `checkh(cond_b, 6'b111111); + end bit cmp; initial begin -`ifndef VERILATOR #1; -`endif // verilator lint_on WIDTH `checkh(bug729_yuu, 1'b0); diff --git a/test_regress/t/t_math_signed3_noopt.py b/test_regress/t/t_math_signed3_noopt.py index 929b1beca..75d6e33d2 100755 --- a/test_regress/t/t_math_signed3_noopt.py +++ b/test_regress/t/t_math_signed3_noopt.py @@ -11,7 +11,9 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["-O0"]) +test.top_filename = "t/t_math_signed3.v" + +test.compile(make_main=False, verilator_flags2=["-O0", "--main", "--exe", "--timing"]) test.execute() diff --git a/test_regress/t/t_math_signed3_noopt.v b/test_regress/t/t_math_signed3_noopt.v deleted file mode 100644 index 959fb3a6d..000000000 --- a/test_regress/t/t_math_signed3_noopt.v +++ /dev/null @@ -1,132 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2014 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -`define stop $stop -`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) - -module t (/*AUTOARG*/); - - // verilator lint_off WIDTH - wire [1:0] bug729_au = ~0; - wire signed [1:0] bug729_as = ~0; - wire [2:0] bug729_b = ~0; - // the $signed output is unsigned because the input is unsigned; the signedness does not change. - wire [0:0] bug729_yuu = $signed(2'b11) == 3'b111; //1'b0 - wire [0:0] bug729_ysu = $signed(2'SB11) == 3'b111; //1'b0 - wire [0:0] bug729_yus = $signed(2'b11) == 3'sb111; //1'b1 - wire [0:0] bug729_yss = $signed(2'sb11) == 3'sb111; //1'b1 - wire [0:0] bug729_zuu = 2'sb11 == 3'b111; //1'b0 - wire [0:0] bug729_zsu = 2'sb11 == 3'b111; //1'b0 - wire [0:0] bug729_zus = 2'sb11 == 3'sb111; //1'b1 - wire [0:0] bug729_zss = 2'sb11 == 3'sb111; //1'b1 - - wire [3:0] bug733_a = 4'b0010; - wire [3:0] bug733_yu = $signed(|bug733_a); // 4'b1111 note | is always unsigned - wire signed [3:0] bug733_ys = $signed(|bug733_a); // 4'b1111 - - wire [3:0] bug733_zu = $signed(2'b11); // 4'b1111 - wire signed [3:0] bug733_zs = $signed(2'sb11); // 4'b1111 - - // When RHS of assignment is fewer bits than lhs, RHS sign or zero extends based on RHS's sign - - wire [3:0] bug733_qu = 2'sb11; // 4'b1111 - wire signed [3:0] bug733_qs = 2'sb11; // 4'b1111 - reg signed [32:0] bug349_s; - reg signed [32:0] bug349_u; - - wire signed [1:0] sb11 = 2'sb11; - - wire [3:0] subout_u; - sub sub (.a(2'sb11), .z(subout_u)); - // initial `checkh(subout_u, 4'b1111); - - wire [5:0] cond_a = 1'b1 ? 3'sb111 : 5'sb11111; - initial `checkh(cond_a, 6'b111111); - wire [5:0] cond_b = 1'b0 ? 3'sb111 : 5'sb11111; - initial `checkh(cond_b, 6'b111111); - - bit cmp; - - initial begin -`ifndef VERILATOR - #1; -`endif - - // verilator lint_on WIDTH - `checkh(bug729_yuu, 1'b0); - `checkh(bug729_ysu, 1'b0); - `checkh(bug729_yus, 1'b1); - `checkh(bug729_yss, 1'b1); - - `checkh(bug729_zuu, 1'b0); - `checkh(bug729_zsu, 1'b0); - `checkh(bug729_zus, 1'b1); - `checkh(bug729_zss, 1'b1); - - // `checkh(bug733_yu, 4'b1111); - // `checkh(bug733_ys, 4'b1111); - - `checkh(bug733_zu, 4'b1111); - `checkh(bug733_zs, 4'b1111); - - `checkh(bug733_qu, 4'b1111); - `checkh(bug733_qs, 4'b1111); - - // verilator lint_off WIDTH - bug349_s = 4'sb1111; - `checkh(bug349_s, 33'h1ffffffff); - bug349_u = 4'sb1111; - `checkh(bug349_u, 33'h1ffffffff); - - bug349_s = 4'sb1111 - 1'b1; - `checkh(bug349_s,33'he); - - bug349_s = 4'sb1111 - 5'b00001; - `checkh(bug349_s,33'he); - - cmp = 3'sb111 == 4'b111; - `checkh(cmp, 1); - cmp = 3'sb111 == 4'sb111; - `checkh(cmp, 0); - cmp = 3'sb111 != 4'b111; - `checkh(cmp, 0); - cmp = 3'sb111 != 4'sb111; - `checkh(cmp, 1); - - cmp = 3'sb111 === 4'b111; - `checkh(cmp, 1); - cmp = 3'sb111 === 4'sb111; - `checkh(cmp, 0); - - case (2'sb11) - 4'b1111: $stop; - default: ; - endcase - - case (sb11) - 4'b1111: $stop; - default: ; - endcase - - case (2'sb11) - 4'sb1111: ; - default: $stop; - endcase - - case (sb11) - 4'sb1111: ; - default: $stop; - endcase - - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule - -module sub(input [3:0] a, - output [3:0] z); - assign z = a; -endmodule From 7191a0ba8bf354021ca9e513aea8ef35691cb34b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 9 May 2025 08:25:54 -0400 Subject: [PATCH 047/211] Internals: Favor preincrements. No functional change. --- include/verilated.cpp | 8 +- include/verilated_cov.cpp | 2 +- include/verilated_probdist.cpp | 2 +- include/verilated_random.cpp | 12 +-- include/verilated_random.h | 2 +- include/verilated_saif_c.cpp | 2 +- include/verilated_trace_imp.h | 2 +- include/verilated_vpi.cpp | 32 +++--- src/V3Number.cpp | 186 ++++++++++++++++----------------- src/V3Number.h | 2 +- src/V3Stats.h | 2 +- 11 files changed, 126 insertions(+), 126 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index 3f9992222..0fc525de1 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -577,14 +577,14 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lw const int owords = VL_WORDS_I(obits); VL_DEBUG_IFDEF(assert(owords <= VL_MULS_MAX_WORDS);); owp[0] = 1; - for (int i = 1; i < VL_WORDS_I(obits); i++) owp[i] = 0; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; // cppcheck-has-bug-suppress variableScope VlWide powstore; // Fixed size, as MSVC++ doesn't allow [words] here VlWide lastpowstore; // Fixed size, as MSVC++ doesn't allow [words] here VlWide lastoutstore; // Fixed size, as MSVC++ doesn't allow [words] here // cppcheck-has-bug-suppress variableScope VL_ASSIGN_W(obits, powstore, lwp); - for (int bit = 0; bit < rbits; bit++) { + for (int bit = 0; bit < rbits; ++bit) { if (bit > 0) { // power = power*power VL_ASSIGN_W(obits, lastpowstore, powstore); VL_MUL_W(owords, powstore, lastpowstore, lastpowstore); @@ -1770,7 +1770,7 @@ std::string VL_STACKTRACE_N() VL_MT_SAFE { if (!strings) return "Unable to backtrace\n"; std::string result = "Backtrace:\n"; - for (int j = 0; j < nptrs; j++) result += std::string{strings[j]} + "\n"s; + for (int j = 0; j < nptrs; ++j) result += std::string{strings[j]} + "\n"s; free(strings); return result; } @@ -2042,7 +2042,7 @@ static const char* formatBinary(int nBits, uint32_t bits) { assert((nBits >= 1) && (nBits <= 32)); static thread_local char t_buf[64]; - for (int i = 0; i < nBits; i++) { + for (int i = 0; i < nBits; ++i) { const bool isOne = bits & (1 << (nBits - 1 - i)); t_buf[i] = (isOne ? '1' : '0'); } diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index bf3153d72..a9bc7c1cd 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -312,7 +312,7 @@ public: const char* fnstartp = m_insertFilenamep; while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1; const char* fnendp = fnstartp; - for (; *fnendp && *fnendp != '.'; fnendp++) {} + for (; *fnendp && *fnendp != '.'; ++fnendp) {} const size_t page_len = fnendp - fnstartp; const std::string page_default = "sp_user/" + std::string{fnstartp, page_len}; ckeyps[2] = "page"; diff --git a/include/verilated_probdist.cpp b/include/verilated_probdist.cpp index 43c436a59..5db32a8de 100644 --- a/include/verilated_probdist.cpp +++ b/include/verilated_probdist.cpp @@ -116,7 +116,7 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE { return 0; } double x = 1.0; - for (int32_t i = 1; i <= k; i++) x = x * _vl_dbase_uniform(seedr, 0, 1); + for (int32_t i = 1; i <= k; ++i) x = x * _vl_dbase_uniform(seedr, 0, 1); const double a = static_cast(mean); const double b = static_cast(k); double r = -a * log(x) / b; diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 30e45d64d..c61c95880 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -112,7 +112,7 @@ public: if (m_pidStatus) { std::stringstream msg; msg << "Subprocess command `" << m_cmd[0]; - for (const char* const* arg = m_cmd + 1; *arg; arg++) msg << ' ' << *arg; + for (const char* const* arg = m_cmd + 1; *arg; ++arg) msg << ' ' << *arg; msg << "' failed: "; if (WIFSIGNALED(m_pidStatus)) msg << strsignal(WTERMSIG(m_pidStatus)) @@ -221,7 +221,7 @@ static Process& getSolver() { static std::vector s_argv; static std::string s_program = Verilated::threadContextp()->solverProgram(); s_argv.emplace_back(&s_program[0]); - for (char* arg = &s_program[0]; *arg; arg++) { + for (char* arg = &s_program[0]; *arg; ++arg) { if (*arg == ' ') { *arg = '\0'; s_argv.emplace_back(arg + 1); @@ -242,7 +242,7 @@ static Process& getSolver() { msg << "Unable to communicate with SAT solver, please check its installation or specify a " "different one in VERILATOR_SOLVER environment variable.\n"; msg << " ... Tried: $"; - for (const char* const* arg = cmd; *arg; arg++) msg << ' ' << *arg; + for (const char* const* arg = cmd; *arg; ++arg) msg << ' ' << *arg; msg << '\n'; const std::string str = msg.str(); VL_WARN_MT("", 0, "randomize", str.c_str()); @@ -294,7 +294,7 @@ void VlRandomVar::emitType(std::ostream& s) const { s << "(_ BitVec " << width() int VlRandomVar::totalWidth() const { return m_width; } static bool parseSMTNum(int obits, WDataOutP owp, const std::string& val) { int i; - for (i = 0; val[i] && val[i] != '#'; i++) {} + for (i = 0; val[i] && val[i] != '#'; ++i) {} if (val[i++] != '#') return false; switch (val[i++]) { case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break; @@ -345,7 +345,7 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) { os << "(= #b"; for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0'); if (bits > 1) os << " (concat"; - for (int i = 0; i < bits; i++) { + for (int i = 0; i < bits; ++i) { IData varBitsLeft = varBits; IData varBitsWant = (varBits + 1) / 2; if (varBits > 2) os << " (bvxor"; @@ -393,7 +393,7 @@ bool VlRandomizer::next(VlRNG& rngr) { f << "(reset)\n"; return false; } - for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; i++) { + for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) { f << "(assert "; randomConstraint(f, rngr, _VL_SOLVER_HASH_LEN); f << ")\n"; diff --git a/include/verilated_random.h b/include/verilated_random.h index f75b199d5..fc08596db 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -143,7 +143,7 @@ public: } void emitGetValue(std::ostream& s) const override { const int elementCounts = countMatchingElements(*m_arrVarsRefp, name()); - for (int i = 0; i < elementCounts; i++) { + for (int i = 0; i < elementCounts; ++i) { const std::string indexed_name = name() + std::to_string(i); const auto it = m_arrVarsRefp->find(indexed_name); if (it != m_arrVarsRefp->end()) { diff --git a/include/verilated_saif_c.cpp b/include/verilated_saif_c.cpp index ee5a40e8e..9e6cd7d9a 100644 --- a/include/verilated_saif_c.cpp +++ b/include/verilated_saif_c.cpp @@ -110,7 +110,7 @@ public: "The emitted value must be of integral type"); const uint64_t dt = time - m_lastTime; - for (size_t i = 0; i < std::min(m_width, bits); i++) { + for (size_t i = 0; i < std::min(m_width, bits); ++i) { m_bits[i].aggregateVal(dt, (newval >> i) & 1); } updateLastTime(time); diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index d13f5bcf6..5f0d2bd87 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -47,7 +47,7 @@ static double timescaleToDouble(const char* unitp) VL_PURE { // On error so we allow just "ns" to return 1e-9. if (value == 0.0 && endp == unitp) value = 1; unitp = endp; - for (; *unitp && std::isspace(*unitp); unitp++) {} + for (; *unitp && std::isspace(*unitp); ++unitp) {} switch (*unitp) { case 's': value *= 1e0; break; case 'm': value *= 1e-3; break; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index c60206abe..5af4bc916 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -205,7 +205,7 @@ public: uint32_t size() const override { const int maxDimNum = maxDim(isIndexedDimUnpacked()); int size = 1; - for (int dim = indexedDim() + 1; dim <= maxDimNum; dim++) + for (int dim = indexedDim() + 1; dim <= maxDimNum; ++dim) size *= varp()->range(dim)->elements(); return size; } @@ -503,7 +503,7 @@ public: explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop) : m_var{new VerilatedVpioVar(vop)} , m_maxDim{vop->varp()->udims() - 1} { - for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) + for (auto it = vop->indexedDim() + 1; it <= m_maxDim; ++it) m_ranges.push_back(*vop->varp()->range(it)); for (auto it : m_ranges) m_nextIndex.push_back(it.right()); } @@ -2219,7 +2219,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { std::vector ranges; const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked()); - for (int dim = vop->indexedDim() + 1; dim <= maxDim; dim++) + for (int dim = vop->indexedDim() + 1; dim <= maxDim; ++dim) ranges.emplace_back(*vop->varp()->range(dim)); // allow one more range layer (regbit) @@ -2945,7 +2945,7 @@ void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsi const unsigned packedSize, const bool leftIsLow, const T* src, K* dst) { static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T"); - for (int i = 0; i < num; i++) { + for (int i = 0; i < num; ++i) { dst[i] = src[index]; index = leftIsLow ? index == (size - 1) ? 0 : index + 1 : index == 0 ? size - 1 @@ -2964,7 +2964,7 @@ void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsi const T mask = element_size_bytes == sizeof(T) ? static_cast(-1) : ~(static_cast(-1) << (element_size_bytes * 8)); - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { dst[index] = src[i] & static_cast(mask); index = leftIsLow ? index == (size - 1) ? 0 : index + 1 : index == 0 ? size - 1 @@ -2981,7 +2981,7 @@ void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsign const unsigned element_size_bytes = VL_BYTES_I(packedSize); const unsigned element_size_words = VL_WORDS_I(packedSize); if (sizeof(T) == sizeof(QData)) { - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { dst[i * 2].aval = static_cast(src[index]); dst[i * 2].bval = 0; dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32; @@ -2991,10 +2991,10 @@ void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsign : index - 1; } } else { - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { const size_t dst_index = i * element_size_words; const size_t src_index = index * element_size_words; - for (unsigned j = 0; j < element_size_words; j++) { + for (unsigned j = 0; j < element_size_words; ++j) { dst[dst_index + j].aval = src[src_index + j]; dst[dst_index + j].bval = 0; } @@ -3017,7 +3017,7 @@ void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsign const QData mask = element_size_bytes == sizeof(T) ? static_cast(-1) : ~(static_cast(-1) << (element_size_bytes * 8)); - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { dst[index] = src[i * 2].aval; dst[index] |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask; @@ -3026,9 +3026,9 @@ void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsign : index - 1; } } else { - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { unsigned bytes_stored = 0; - for (unsigned j = 0; j < element_size_words; j++) { + for (unsigned j = 0; j < element_size_words; ++j) { if (bytes_stored >= element_size_bytes) break; const T mask = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32) @@ -3057,9 +3057,9 @@ void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned siz while (num-- > 0) { const size_t src_offset = index * element_size_repr; unsigned bytes_copied = 0; - for (unsigned j = 0; j < element_size_repr; j++) { + for (unsigned j = 0; j < element_size_repr; ++j) { const T& src_data = src[src_offset + j]; - for (unsigned k = 0; k < sizeof(T); k++) { + for (unsigned k = 0; k < sizeof(T); ++k) { if (bytes_copied++ == element_size_bytes) break; dst[dst_index++] = src_data >> (k * 8); } @@ -3080,13 +3080,13 @@ void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsign const bool fourState, const PLI_UBYTE8* src, T* dst) { const unsigned element_size_bytes VL_BYTES_I(packedSize); const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); - for (unsigned i = 0; i < num; i++) { + for (unsigned i = 0; i < num; ++i) { unsigned bytes_copied = 0; const size_t dst_offset = index * element_size_repr; const size_t src_offset = i * element_size_bytes; - for (unsigned j = 0; j < element_size_repr; j++) { + for (unsigned j = 0; j < element_size_repr; ++j) { T& dst_data = dst[dst_offset + j]; - for (unsigned k = 0; k < sizeof(T); k++) { + for (unsigned k = 0; k < sizeof(T); ++k) { if (bytes_copied == element_size_bytes) break; const unsigned src_index = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied; diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 9d9d3a2e1..f0280076c 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -128,7 +128,7 @@ V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) { void V3Number::create(const char* sourcep) { m_data.setLogic(); const char* value_startp = sourcep; - for (const char* cp = sourcep; *cp; cp++) { + for (const char* cp = sourcep; *cp; ++cp) { if (*cp == '\'') { value_startp = cp + 1; break; @@ -141,21 +141,21 @@ void V3Number::create(const char* sourcep) { if (value_startp != sourcep) { // Has a ' string widthn; const char* cp = sourcep; - for (; *cp; cp++) { + for (; *cp; ++cp) { if (*cp == '\'') { - cp++; + ++cp; break; } if (*cp != '_') widthn += *cp; } while (*cp == '_') cp++; if (*cp && std::tolower(*cp) == 's') { - cp++; + ++cp; isSigned(true); } if (*cp) { base = *cp; - cp++; + ++cp; } value_startp = cp; @@ -221,14 +221,14 @@ void V3Number::create(const char* sourcep) { int base_align = 1; if (std::tolower(base) == 'd') { // Ignore leading zeros so we don't issue too many digit errors when lots of leading 0's - while (*value_startp == '_' || *value_startp == '0') value_startp++; + while (*value_startp == '_' || *value_startp == '0') ++value_startp; // Convert decimal number to hex int olen = 0; uint32_t val = 0; int got_x = 0; int got_z = 0; int got_01 = 0; - for (const char* cp = value_startp; *cp; cp++) { + for (const char* cp = value_startp; *cp; ++cp) { switch (std::tolower(*cp)) { case '0': // FALLTHRU case '1': // FALLTHRU @@ -254,10 +254,10 @@ void V3Number::create(const char* sourcep) { opAdd(product, addend); if (product.bitsValue(width(), 4)) { // Overflowed warnTooMany(sourcep); - while (*(cp + 1)) cp++; // Skip ahead so don't get multiple warnings + while (*(cp + 1)) ++cp; // Skip ahead so don't get multiple warnings } } - olen++; + ++olen; got_01 = 1; break; } @@ -344,7 +344,7 @@ void V3Number::create(const char* sourcep) { case '7': setBit(obit++,1); setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); break; case '8': setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); break; case '9': setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); break; - case 'a': setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); break; + case 'a': setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); break; case 'b': setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); break; case 'c': setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); break; case 'd': setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); break; @@ -379,7 +379,7 @@ void V3Number::create(const char* sourcep) { // This fixes 2'bx to become 2'bxx. while (obit <= width() && obit && bitIsXZ(obit - 1)) { setBit(obit, bitIs(obit - 1)); - obit++; + ++obit; } opCleanThis(true); @@ -428,24 +428,24 @@ int V3Number::log2bQuad(uint64_t num) { // Setters V3Number& V3Number::setZero() { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; return *this; } V3Number& V3Number::setQuad(uint64_t value) { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; m_data.num()[0].m_value = value & 0xffffffffULL; if (width() > 32) m_data.num()[1].m_value = (value >> 32ULL) & 0xffffffffULL; opCleanThis(); return *this; } V3Number& V3Number::setLong(uint32_t value) { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; m_data.num()[0].m_value = value; opCleanThis(); return *this; } V3Number& V3Number::setLongS(int32_t value) { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; union { uint32_t u; int32_t s; @@ -465,35 +465,35 @@ V3Number& V3Number::setDouble(double value) { } u; u.d = value; (void)u.d; - for (int i = 2; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 2; i < words(); ++i) m_data.num()[i] = {0, 0}; m_data.num()[0].m_value = u.u[0]; m_data.num()[1].m_value = u.u[1]; return *this; } V3Number& V3Number::setSingleBits(char value) { - for (int i = 1 /*upper*/; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 1 /*upper*/; i < words(); ++i) m_data.num()[i] = {0, 0}; m_data.num()[0] = {(value == '1' || value == 'x' || value == 1 || value == 3), (value == 'z' || value == 'x' || value == 2 || value == 3)}; return *this; } V3Number& V3Number::setAllBits0() { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0}; return *this; } V3Number& V3Number::setAllBits1() { - for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, 0}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {~0U, 0}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsX() { // Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero - for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, ~0U}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {~0U, ~0U}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsZ() { - for (int i = 0; i < words(); i++) m_data.num()[i] = {0, ~0U}; + for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, ~0U}; opCleanThis(); return *this; } @@ -512,7 +512,7 @@ V3Number& V3Number::setAllBitsXRemoved() { } V3Number& V3Number::setValue1() { m_data.num()[0] = {1, 0}; - for (int i = 1; i < words(); i++) m_data.num()[i] = {0, 0}; + for (int i = 1; i < words(); ++i) m_data.num()[i] = {0, 0}; return *this; } @@ -524,7 +524,7 @@ void V3Number::setBitX0(int bit) { V3Number& V3Number::setMask(int nbits, int lsb) { setZero(); - for (int bit = lsb; bit < lsb + nbits; bit++) setBit(bit, 1); + for (int bit = lsb; bit < lsb + nbits; ++bit) setBit(bit, 1); return *this; } @@ -759,7 +759,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB // Spec says always drop leading zeros, this isn't quite right, we space pad. int bit = width() - 1; bool start = true; - while ((bit % 8) != 7) bit++; + while ((bit % 8) != 7) ++bit; for (; bit >= 0; bit -= 8) { const int v = bitsValue(bit - 7, 8); if (!start || v) { @@ -784,7 +784,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB // a very wide mantissa, we use log2(2**mantissabits)/log2(10), // which is (+1.0 is for rounding bias): double dchars = mantissabits / 3.321928094887362 + 1.0; - if (issigned) dchars++; // space for sign + if (issigned) ++dchars; // space for sign fmtsize = cvtToStr(int(dchars)); } bool hasXZ = false; @@ -833,7 +833,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB // 'l' // Library - converted to text by V3LinkResolve // 'p' // Packed - converted to another code by V3Width case 'u': { // Packed 2-state - for (int i = 0; i < words(); i++) { + for (int i = 0; i < words(); ++i) { const uint32_t v = m_data.num()[i].m_value; str += static_cast((v >> 0) & 0xff); str += static_cast((v >> 8) & 0xff); @@ -843,7 +843,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const VL_MT_STAB return str; } case 'z': { // Packed 4-state - for (int i = 0; i < words(); i++) { + for (int i = 0; i < words(); ++i) { const ValueAndX v = m_data.num()[i]; str += static_cast((v.m_value >> 0) & 0xff); str += static_cast((v.m_value >> 8) & 0xff); @@ -1039,7 +1039,7 @@ uint8_t V3Number::dataByte(int byte) const { bool V3Number::isAllZ() const VL_MT_SAFE { if (isDouble() || isString()) return false; - for (int i = 0; i < width(); i++) { + for (int i = 0; i < width(); ++i) { if (!bitIsZ(i)) return false; } return true; @@ -1056,7 +1056,7 @@ bool V3Number::isAllX() const VL_MT_SAFE { } bool V3Number::isEqZero() const VL_MT_SAFE { if (isString()) return m_data.str().empty(); - for (int i = 0; i < words(); i++) { + for (int i = 0; i < words(); ++i) { const ValueAndX v = m_data.num()[i]; if (v.m_value || v.m_valueX) return false; } @@ -1064,21 +1064,21 @@ bool V3Number::isEqZero() const VL_MT_SAFE { } bool V3Number::isNeqZero() const { if (isString()) return !m_data.str().empty(); - for (int i = 0; i < words(); i++) { + for (int i = 0; i < words(); ++i) { const ValueAndX v = m_data.num()[i]; if (v.m_value & ~v.m_valueX) return true; } return false; } bool V3Number::isBitsZero(int msb, int lsb) const { - for (int i = lsb; i <= msb; i++) { + for (int i = lsb; i <= msb; ++i) { if (VL_UNLIKELY(!bitIs0(i))) return false; } return true; } bool V3Number::isEqOne() const { if (m_data.num()[0].m_value != 1 || m_data.num()[0].m_valueX) return false; - for (int i = 1; i < words(); i++) { + for (int i = 1; i < words(); ++i) { const ValueAndX v = m_data.num()[i]; if (v.m_value || v.m_valueX) return false; } @@ -1087,7 +1087,7 @@ bool V3Number::isEqOne() const { bool V3Number::isEqAllOnes(int optwidth) const { // Correct number of zero bits/width matters if (!optwidth) optwidth = width(); - for (int bit = 0; bit < optwidth; bit++) { + for (int bit = 0; bit < optwidth; ++bit) { if (!bitIs1(bit)) return false; } return true; @@ -1101,7 +1101,7 @@ bool V3Number::isFourState() const VL_MT_SAFE { } bool V3Number::isAnyX() const VL_MT_SAFE { if (isDouble() || isString()) return false; - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (bitIsX(bit)) return true; } return false; @@ -1109,14 +1109,14 @@ bool V3Number::isAnyX() const VL_MT_SAFE { bool V3Number::isAnyXZ() const { return isAnyX() || isAnyZ(); } bool V3Number::isAnyZ() const VL_MT_SAFE { if (isDouble() || isString()) return false; - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (bitIsZ(bit)) return true; } return false; } bool V3Number::isLtXZ(const V3Number& rhs) const { // Include X/Z in comparisons for sort ordering - for (int bit = 0; bit < std::max(width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(width(), rhs.width()); ++bit) { if (bitIs1(bit) && rhs.bitIs0(bit)) return true; if (rhs.bitIs1(bit) && bitIs0(bit)) return false; if (bitIsXZ(bit)) return true; @@ -1183,8 +1183,8 @@ uint32_t V3Number::countBits(const V3Number& ctrl1, const V3Number& ctrl2, uint32_t V3Number::countOnes() const { int n = 0; - for (int bit = 0; bit < width(); bit++) { - if (bitIs1(bit)) n++; + for (int bit = 0; bit < width(); ++bit) { + if (bitIs1(bit)) ++n; } return n; } @@ -1203,7 +1203,7 @@ V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) setBit(bit, 1); } return *this; @@ -1213,7 +1213,7 @@ V3Number& V3Number::opBitsOne(const V3Number& lhs) { // 1->1, 0/X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs1(bit)) setBit(bit, 1); } return *this; @@ -1223,7 +1223,7 @@ V3Number& V3Number::opBitsXZ(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIsXZ(bit)) setBit(bit, 1); } return *this; @@ -1233,7 +1233,7 @@ V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0 NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIsZ(bit)) setBit(bit, 1); } return *this; @@ -1247,7 +1247,7 @@ V3Number& V3Number::opRedOr(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); char outc = 0; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs1(bit)) { return setSingleBits(1); } else if (lhs.bitIs0(bit)) { @@ -1264,7 +1264,7 @@ V3Number& V3Number::opRedAnd(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); char outc = 1; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs0(bit)) { return setSingleBits(0); } else if (lhs.bitIs1(bit)) { @@ -1280,7 +1280,7 @@ V3Number& V3Number::opRedXor(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); char outc = 0; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs1(bit)) { if (outc == 1) { outc = 0; @@ -1349,7 +1349,7 @@ V3Number& V3Number::opLogNot(const V3Number& lhs) { NUM_ASSERT_LOGIC_ARGS1(lhs); // op i, 1 bit return char outc = 1; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs1(bit)) { outc = 0; goto last; @@ -1368,7 +1368,7 @@ V3Number& V3Number::opNot(const V3Number& lhs) { NUM_ASSERT_LOGIC_ARGS1(lhs); // op i, L(lhs) bit return setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs0(bit)) { setBit(bit, 1); } else if (lhs.bitIsXZ(bit)) { @@ -1383,7 +1383,7 @@ V3Number& V3Number::opAnd(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) { // 0 @@ -1399,7 +1399,7 @@ V3Number& V3Number::opOr(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) { @@ -1416,7 +1416,7 @@ V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { setBit(bit, 1); } else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { @@ -1439,13 +1439,13 @@ V3Number& V3Number::opConcat(const V3Number& lhs, const V3Number& rhs) { v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); } int obit = 0; - for (int bit = 0; bit < rhs.width(); bit++) { + for (int bit = 0; bit < rhs.width(); ++bit) { setBit(obit, rhs.bitIs(bit)); - obit++; + ++obit; } - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { setBit(obit, lhs.bitIs(bit)); - obit++; + ++obit; } return *this; } @@ -1504,7 +1504,7 @@ V3Number& V3Number::opStreamL(const V3Number& lhs, const V3Number& rhs) { const int ssize = std::min(rhs.toUInt(), static_cast(lhs.width())); for (int istart = 0; istart < lhs.width(); istart += ssize) { const int ostart = std::max(0, lhs.width() - ssize - istart); - for (int bit = 0; bit < ssize && bit < lhs.width() - istart; bit++) { + for (int bit = 0; bit < ssize && bit < lhs.width() - istart; ++bit) { setBit(ostart + bit, lhs.bitIs(istart + bit)); } } @@ -1517,14 +1517,14 @@ V3Number& V3Number::opLogAnd(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); char loutc = 0; char routc = 0; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs1(bit)) { loutc = 1; break; } if (lhs.bitIsXZ(bit) && loutc == 0) loutc = 'x'; } - for (int bit = 0; bit < rhs.width(); bit++) { + for (int bit = 0; bit < rhs.width(); ++bit) { if (rhs.bitIs1(bit)) { routc = 1; break; @@ -1542,14 +1542,14 @@ V3Number& V3Number::opLogOr(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); char outc = 0; - for (int bit = 0; bit < lhs.width(); bit++) { + for (int bit = 0; bit < lhs.width(); ++bit) { if (lhs.bitIs1(bit)) { outc = 1; goto last; } if (lhs.bitIsXZ(bit) && outc == 0) outc = 'x'; } - for (int bit = 0; bit < rhs.width(); bit++) { + for (int bit = 0; bit < rhs.width(); ++bit) { if (rhs.bitIs1(bit)) { outc = 1; goto last; @@ -1658,7 +1658,7 @@ V3Number& V3Number::opEq(const V3Number& lhs, const V3Number& rhs) { if (lhs.isString()) return opEqN(lhs, rhs); if (lhs.isDouble()) return opEqD(lhs, rhs); char outc = 1; - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc = 0; goto last; @@ -1680,7 +1680,7 @@ V3Number& V3Number::opNeq(const V3Number& lhs, const V3Number& rhs) { if (lhs.isString()) return opNeqN(lhs, rhs); if (lhs.isDouble()) return opNeqD(lhs, rhs); char outc = 0; - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc = 1; goto last; @@ -1721,7 +1721,7 @@ V3Number& V3Number::opCaseNeq(const V3Number& lhs, const V3Number& rhs) { } else if (lhs.isDouble()) { return opNeqD(lhs, rhs); } - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc = 1; goto last; @@ -1735,7 +1735,7 @@ V3Number& V3Number::opWildEq(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); char outc = 1; - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (!rhs.bitIsXZ(bit)) { if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc = 0; @@ -1752,7 +1752,7 @@ V3Number& V3Number::opWildNeq(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); char outc = 0; - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (!rhs.bitIsXZ(bit)) { if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc = 1; @@ -1770,7 +1770,7 @@ V3Number& V3Number::opGt(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_OP_ARGS2(lhs, rhs); NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); char outc = 0; - for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) { + for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); ++bit) { if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) outc = 1; if (rhs.bitIs1(bit) && lhs.bitIs0(bit)) outc = 0; if (lhs.bitIsXZ(bit)) outc = 'x'; @@ -1796,7 +1796,7 @@ V3Number& V3Number::opGtS(const V3Number& lhs, const V3Number& rhs) { outc = 0; // - !> + } else { // both positive or negative, normal > - for (int bit = 0; bit < std::max(lhs.width() - 1, rhs.width() - 1); bit++) { + for (int bit = 0; bit < std::max(lhs.width() - 1, rhs.width() - 1); ++bit) { if (lhs.bitIs1Extend(bit) && rhs.bitIs0(bit)) outc = 1; if (rhs.bitIs1Extend(bit) && lhs.bitIs0(bit)) outc = 0; if (lhs.bitIsXZ(bit)) outc = 'x'; @@ -1834,12 +1834,12 @@ V3Number& V3Number::opShiftR(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); if (rhs.isFourState()) return setAllBitsX(); setZero(); - for (int bit = 32; bit < rhs.width(); bit++) { + for (int bit = 32; bit < rhs.width(); ++bit) { if (rhs.bitIs1(bit)) return *this; // shift of over 2^32 must be zero } const uint32_t rhsval = rhs.toUInt(); if (rhsval < static_cast(lhs.width())) { - for (int bit = 0; bit < width(); bit++) setBit(bit, lhs.bitIs(bit + rhsval)); + for (int bit = 0; bit < width(); ++bit) setBit(bit, lhs.bitIs(bit + rhsval)); } return *this; } @@ -1853,8 +1853,8 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); if (rhs.isFourState()) return setAllBitsX(); setZero(); - for (int bit = 32; bit < rhs.width(); bit++) { - for (int sbit = 0; sbit < width(); sbit++) { + for (int bit = 32; bit < rhs.width(); ++bit) { + for (int sbit = 0; sbit < width(); ++sbit) { setBit(sbit, lhs.bitIs(lbits - 1)); // 0/1/X/Z } if (rhs.bitIs1(lbits - 1)) setAllBits1(); // -1 else 0 @@ -1862,11 +1862,11 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t } const uint32_t rhsval = rhs.toUInt(); if (rhsval < static_cast(lhs.width())) { - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits)); } } else { - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { setBit(bit, lhs.bitIs(lbits - 1)); // 0/1/X/Z } } @@ -1879,11 +1879,11 @@ V3Number& V3Number::opShiftL(const V3Number& lhs, const V3Number& rhs) { NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); if (rhs.isFourState()) return setAllBitsX(); setZero(); - for (int bit = 32; bit < rhs.width(); bit++) { + for (int bit = 32; bit < rhs.width(); ++bit) { if (rhs.bitIs1(bit)) return *this; // shift of over 2^32 must be zero } const uint32_t rhsval = rhs.toUInt(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (bit >= static_cast(rhsval)) setBit(bit, lhs.bitIs(bit - rhsval)); } return *this; @@ -1912,7 +1912,7 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) { setZero(); // Addem uint64_t carry = 0; - for (int word = 0; word < words(); word++) { + for (int word = 0; word < words(); ++word) { const uint64_t lwordval = lhs.m_data.num()[word].m_value; const uint64_t rwordval = rhs.m_data.num()[word].m_value; const uint64_t sum = lwordval + rwordval + carry; @@ -1942,14 +1942,14 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) { setQuad(lhs.toUQuad() * rhs.toUQuad()); opCleanThis(); // Mult produces extra bits in result } else { - for (int lword = 0; lword < lhs.words(); lword++) { + for (int lword = 0; lword < lhs.words(); ++lword) { const uint64_t lwordval = static_cast(lhs.m_data.num()[lword].m_value); if (lwordval == 0) continue; - for (int rword = 0; rword < rhs.words(); rword++) { + for (int rword = 0; rword < rhs.words(); ++rword) { const uint64_t rwordval = static_cast(rhs.m_data.num()[rword].m_value); if (rwordval == 0) continue; uint64_t mul = lwordval * rwordval; - for (int qword = lword + rword; qword < words(); qword++) { + for (int qword = lword + rword; qword < words(); ++qword) { mul += static_cast(m_data.num()[qword].m_value); m_data.num()[qword].m_value = (mul & 0xffffffffULL); mul = (mul >> 32ULL) & 0xffffffffULL; @@ -2093,8 +2093,8 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized // Zero for ease of debugging and to save having to zero for shifts - for (int i = 0; i < words; i++) m_data.num()[i].m_value = 0; - for (int i = 0; i < words + 1; i++) { un[i] = vn[i] = 0; } // +1 as vn may get extra word + for (int i = 0; i < words; ++i) m_data.num()[i].m_value = 0; + for (int i = 0; i < words + 1; ++i) { un[i] = vn[i] = 0; } // +1 as vn may get extra word // Algorithm requires divisor MSB to be set // Copy and shift to normalize divisor so MSB of vn[vw-1] is set @@ -2139,7 +2139,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool int64_t t = 0; // Must be signed uint64_t k = 0; - for (int i = 0; i < vw; i++) { + for (int i = 0; i < vw; ++i) { const uint64_t p = qhat * vn[i]; // Multiply by estimate t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract un[i + j] = t; @@ -2153,7 +2153,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool // Over subtracted; correct by adding back m_data.num()[j].m_value--; k = 0; - for (int i = 0; i < vw; i++) { + for (int i = 0; i < vw; ++i) { t = static_cast(un[i + j]) + static_cast(vn[i]) + k; un[i + j] = t; k = t >> 32ULL; @@ -2168,10 +2168,10 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool if (is_modulus) { // modulus // Need to reverse normalization on copy to output - for (int i = 0; i < vw; i++) { + for (int i = 0; i < vw; ++i) { m_data.num()[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s))); } - for (int i = vw; i < words; i++) m_data.num()[i].m_value = 0; + for (int i = vw; i < words; ++i) m_data.num()[i].m_value = 0; opCleanThis(); UINFO(9, " opmoddiv-mod " << lhs << " " << rhs << " now=" << *this << endl); return *this; @@ -2209,7 +2209,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign, m_data.num()[0].m_value = 1; V3Number power(&lhs, width()); power.opAssign(lhs); - for (int bit = 0; bit < rhs.width(); bit++) { + for (int bit = 0; bit < rhs.width(); ++bit) { if (bit > 0) { // power = power*power V3Number lastPower(&lhs, width()); lastPower.opAssign(power); @@ -2241,7 +2241,7 @@ V3Number& V3Number::opBufIf1(const V3Number& ens, const V3Number& if1s) { NUM_ASSERT_OP_ARGS2(ens, if1s); NUM_ASSERT_LOGIC_ARGS2(ens, if1s); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (ens.bitIs1(bit)) { setBit(bit, if1s.bitIs(bit)); } else { @@ -2273,7 +2273,7 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) { } else if (lhs.isDouble()) { setDouble(lhs.toDouble()); } else { - for (int bit = 0; bit < this->width(); bit++) { + for (int bit = 0; bit < this->width(); ++bit) { setBit(bit, ignoreXZ ? lhs.bitIs1(bit) : lhs.bitIs(bit)); } } @@ -2302,7 +2302,7 @@ V3Number& V3Number::opExtendS(const V3Number& lhs, uint32_t lbits) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { const char extendWith = lhs.bitIsExtend(bit, lbits); setBit(bit, extendWith); } @@ -2314,7 +2314,7 @@ V3Number& V3Number::opExtendXZ(const V3Number& lhs, uint32_t lbits) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); - for (int bit = 0; bit < width(); bit++) setBit(bit, lhs.bitIsExtend(bit, lbits)); + for (int bit = 0; bit < width(); ++bit) setBit(bit, lhs.bitIsExtend(bit, lbits)); return *this; } @@ -2347,7 +2347,7 @@ V3Number& V3Number::opSel(const V3Number& lhs, uint32_t msbval, uint32_t lsbval) NUM_ASSERT_LOGIC_ARGS1(lhs); setZero(); int ibit = lsbval; - for (int bit = 0; bit < width(); bit++) { + for (int bit = 0; bit < width(); ++bit) { if (ibit >= 0 && ibit < lhs.width() && ibit <= static_cast(msbval)) { setBit(bit, lhs.bitIs(ibit)); } else { @@ -2368,13 +2368,13 @@ V3Number& V3Number::opSelInto(const V3Number& lhs, int lsbval, int width) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); int ibit = 0; - for (int bit = lsbval; bit < lsbval + width; bit++) { + for (int bit = lsbval; bit < lsbval + width; ++bit) { if (ibit >= 0 && ibit < lhs.width()) { setBit(bit, lhs.bitIs(ibit)); } else { setBitX0(bit); } - ibit++; + ++ibit; } return *this; } @@ -2544,7 +2544,7 @@ V3Number& V3Number::opReplN(const V3Number& lhs, uint32_t rhsval) { NUM_ASSERT_STRING_ARGS1(lhs); string out; out.reserve(lhs.toString().length() * rhsval); - for (unsigned times = 0; times < rhsval; times++) out += lhs.toString(); + for (unsigned times = 0; times < rhsval; ++times) out += lhs.toString(); return setString(out); } V3Number& V3Number::opToLowerN(const V3Number& lhs) { diff --git a/src/V3Number.h b/src/V3Number.h index d092e8f96..755fdc134 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -628,7 +628,7 @@ public: bool isFourState() const VL_MT_SAFE; bool hasZ() const { if (isString()) return false; - for (int i = 0; i < words(); i++) { + for (int i = 0; i < words(); ++i) { const ValueAndX v = m_data.num()[i]; if ((~v.m_value) & v.m_valueX) return true; } diff --git a/src/V3Stats.h b/src/V3Stats.h index 5ca56cc29..6cf5eddb9 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -46,7 +46,7 @@ public: } VDouble0 operator++(int) { // postfix VDouble0 old = *this; - m_d++; + ++m_d; return old; } VDouble0& operator=(const double v) { From 5d0fd8b9a7e94906948c6d818c77a660e42b30d4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 9 May 2025 21:22:17 -0400 Subject: [PATCH 048/211] Commentary --- src/V3Unknown.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index dd2683b76..5ad89aece 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -50,16 +50,18 @@ class UnknownVisitor final : public VNVisitor { const VNUser2InUse m_inuser2; static const std::string m_xrandPrefix; - // STATE + // STATE - across all visitors + VDouble0 m_statUnkVars; // Statistic tracking + V3UniqueNames m_lvboundNames; // For generating unique temporary variable names + std::unique_ptr m_xrandNames; // For generating unique temporary variable names + + // STATE - for current visit position (use VL_RESTORER) AstNodeModule* m_modp = nullptr; // Current module AstAssignW* m_assignwp = nullptr; // Current assignment AstAssignDly* m_assigndlyp = nullptr; // Current assignment AstNode* m_timingControlp = nullptr; // Current assignment's intra timing control bool m_constXCvt = false; // Convert X's bool m_allowXUnique = true; // Allow unique assignments - VDouble0 m_statUnkVars; // Statistic tracking - V3UniqueNames m_lvboundNames; // For generating unique temporary variable names - std::unique_ptr m_xrandNames; // For generating unique temporary variable names // METHODS From d22608a49fe1818170777f1ecf475ffc4d1d8224 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 9 May 2025 22:00:29 -0400 Subject: [PATCH 049/211] Internals: Refactor common V3JsonFile. No functional change. --- src/V3EmitMkJson.cpp | 206 ++++++++++--------------------------------- src/V3File.h | 131 +++++++++++++++++++++++---- 2 files changed, 163 insertions(+), 174 deletions(-) diff --git a/src/V3EmitMkJson.cpp b/src/V3EmitMkJson.cpp index 91b9dcf9b..d42c57ad3 100644 --- a/src/V3EmitMkJson.cpp +++ b/src/V3EmitMkJson.cpp @@ -30,111 +30,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Emit statements class V3EmitMkJsonEmitter final { - class Printer final { - // MEMBERS - private: - const std::unique_ptr& m_of; - std::stack - m_scope; // Stack of ']' and '}' to be used to close currently open scopes - std::string m_prefix; // Prefix emitted before each line in the current scope (indent * - // scope depth) - std::string m_indent; // Single indent - bool m_empty = true; // Indicates that the current scope is empty - - // METHODS - public: - explicit Printer(const std::unique_ptr& of, - const std::string& indent = " ") - : m_of(of) - , m_indent(indent) { - begin(); - } - - ~Printer() { end(); } - - Printer& begin(const std::string& name, char type = '{') { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << type << "\n"; - m_prefix += m_indent; - m_scope.push(type == '{' ? '}' : ']'); - m_empty = true; - return *this; - } - - Printer& put(const std::string& name, const std::string& value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": \"" << value << "\""; - m_empty = false; - return *this; - } - - Printer& put(const std::string& name, bool value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << (value ? "true" : "false"); - m_empty = false; - return *this; - } - - Printer& put(const std::string& name, int value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << value; - m_empty = false; - return *this; - } - - Printer& begin(char type = '{') { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << type << "\n"; - m_prefix += m_indent; - m_scope.push(type == '{' ? '}' : ']'); - m_empty = true; - return *this; - } - - Printer& put(const std::string& value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << value << "\""; - m_empty = false; - return *this; - } - - Printer& put(bool value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << (value ? "true" : "false"); - m_empty = false; - return *this; - } - - Printer& put(int value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << value; - m_empty = false; - return *this; - } - - template - Printer& putList(const std::string& name, const T& list) { - if (list.empty()) return *this; - begin(name, '['); - for (auto it = list.begin(); it != list.end(); ++it) put(*it); - return end(); - } - - Printer& end() { - assert(m_prefix.length() >= m_indent.length()); - m_prefix.erase(m_prefix.end() - m_indent.length(), m_prefix.end()); - assert(!m_scope.empty()); - *m_of << "\n" << m_prefix << m_scope.top(); - m_scope.pop(); - return *this; - } - - Printer& operator+=(Printer& cursor) { - // Meaningless syntax sugar, at least for now - return *this; - } - }; - // METHODS // STATIC FUNCTIONS @@ -142,8 +37,7 @@ class V3EmitMkJsonEmitter final { const std::string makeDir = V3Os::filenameSlashPath(V3Os::filenameRealPath(v3Global.opt.makeDir())); - const std::unique_ptr of{ - V3File::new_ofstream(makeDir + "/" + v3Global.opt.prefix() + ".json")}; + V3OutJsonFile of{makeDir + "/" + v3Global.opt.prefix() + ".json"}; std::vector classesFast; std::vector classesSlow; @@ -188,41 +82,40 @@ class V3EmitMkJsonEmitter final { for (const auto& cppFile : v3Global.opt.cppFiles()) cppFiles.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(cppFile))); - Printer manifest(of); - Printer& cursor = manifest.put("version", 1) - .begin("system") - .put("perl", V3Options::getenvPERL()) - .put("python3", V3Options::getenvPYTHON3()) - .put("verilator_root", verilatorRoot) - .put("verilator_solver", V3Options::getenvVERILATOR_SOLVER()) - .end() - .begin("options") - .putList("cflags", v3Global.opt.cFlags()) - .putList("ldflags", v3Global.opt.ldLibs()) - .put("system_c", v3Global.opt.systemC()) - .put("coverage", v3Global.opt.coverage()) - .put("use_timing", v3Global.usesTiming()) - .put("threads", v3Global.opt.threads()) - .put("trace", v3Global.opt.trace()) - .put("trace_fst", v3Global.opt.traceEnabledFst()) - .put("trace_saif", v3Global.opt.traceEnabledSaif()) - .put("trace_vcd", v3Global.opt.traceEnabledVcd()) - .end() - .begin("sources") - .putList("global", global) - .putList("classes_slow", classesSlow) - .putList("classes_fast", classesFast) - .putList("support_slow", supportSlow) - .putList("support_fast", supportFast) - .putList("deps", deps) - .putList("user_classes", cppFiles) - .end(); + of.put("version", 1) + .begin("system") + .put("perl", V3Options::getenvPERL()) + .put("python3", V3Options::getenvPYTHON3()) + .put("verilator_root", verilatorRoot) + .put("verilator_solver", V3Options::getenvVERILATOR_SOLVER()) + .end() + .begin("options") + .putList("cflags", v3Global.opt.cFlags()) + .putList("ldflags", v3Global.opt.ldLibs()) + .put("system_c", v3Global.opt.systemC()) + .put("coverage", v3Global.opt.coverage()) + .put("use_timing", v3Global.usesTiming()) + .put("threads", v3Global.opt.threads()) + .put("trace", v3Global.opt.trace()) + .put("trace_fst", v3Global.opt.traceEnabledFst()) + .put("trace_saif", v3Global.opt.traceEnabledSaif()) + .put("trace_vcd", v3Global.opt.traceEnabledVcd()) + .end() + .begin("sources") + .putList("global", global) + .putList("classes_slow", classesSlow) + .putList("classes_fast", classesFast) + .putList("support_slow", supportSlow) + .putList("support_fast", supportFast) + .putList("deps", deps) + .putList("user_classes", cppFiles) + .end(); if (const V3HierBlockPlan* const planp = v3Global.hierPlanp()) { // Sorted hierarchical blocks in order of leaf-first. const V3HierBlockPlan::HierVector& hierBlocks = planp->hierBlocksSorted(); - cursor += cursor.begin("submodules", '['); + of.begin("submodules", '['); for (V3HierBlockPlan::HierVector::const_iterator it = hierBlocks.begin(); it != hierBlocks.end(); ++it) { @@ -247,17 +140,16 @@ class V3EmitMkJsonEmitter final { std::vector cflags; cflags.emplace_back("-fPIC"); - cursor += cursor.begin() - .put("prefix", hblockp->hierPrefix()) - .put("top", hblockp->modp()->name()) - .putList("deps", childDeps) - .put("directory", makeDir + "/" + hblockp->hierPrefix()) - .putList("sources", sources) - .putList("cflags", cflags) - .put("verilator_args", - V3Os::filenameSlashPath( - V3Os::filenameRealPath(hblockp->commandArgsFilename(true)))) - .end(); + of.begin() + .put("prefix", hblockp->hierPrefix()) + .put("top", hblockp->modp()->name()) + .putList("deps", childDeps) + .put("directory", makeDir + "/" + hblockp->hierPrefix()) + .putList("sources", sources) + .putList("cflags", cflags) + .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( + hblockp->commandArgsFilename(true)))) + .end(); } std::vector sources; @@ -268,15 +160,15 @@ class V3EmitMkJsonEmitter final { for (const string& i : vFiles) sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i))); - cursor += cursor.begin() - .put("prefix", v3Global.opt.prefix()) - .put("top", v3Global.rootp()->topModulep()->name()) - .put("directory", makeDir) - .putList("sources", sources) - .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( - planp->topCommandArgsFilename(true)))) - .end() - .end(); + of.begin() + .put("prefix", v3Global.opt.prefix()) + .put("top", v3Global.rootp()->topModulep()->name()) + .put("directory", makeDir) + .putList("sources", sources) + .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( + planp->topCommandArgsFilename(true)))) + .end() + .end(); } } diff --git a/src/V3File.h b/src/V3File.h index b76dad9b1..3a28ef92e 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -109,12 +109,7 @@ class V3OutFormatter VL_NOT_FINAL { static constexpr int MAXSPACE = 80; // After this indent, stop indenting more public: enum AlignClass : uint8_t { AL_AUTO = 0, AL_STATIC = 1 }; - enum Language : uint8_t { - LA_C = 0, - LA_VERILOG = 1, - LA_MK = 2, - LA_XML = 3, - }; + enum Language : uint8_t { LA_C, LA_JSON, LA_MK, LA_VERILOG, LA_XML }; private: // MEMBERS @@ -286,6 +281,119 @@ public: } }; +class V3OutJsonFile final : public V3OutFile { + // CONSTANTS + static constexpr const char* INDENT = " "; // Single indent (4, per JSON std) + + // MEMBERS +private: + std::stack m_scope; // Stack of ']' and '}' to close currently open scopes + std::string m_prefix; // Prefix emitted before each line in current scope + bool m_empty = true; // Current scope is empty, no comma later + +public: + explicit V3OutJsonFile(const string& filename) + : V3OutFile{filename, V3OutFormatter::LA_JSON} { + begin(); + } + ~V3OutJsonFile() override { end(); } + virtual void putsHeader() {} + void puts(const char* strg) { putsNoTracking(strg); } + void puts(const string& strg) { putsNoTracking(strg); } + + // METHODS + V3OutJsonFile& begin(const std::string& name, char type = '{') { + comma(); + puts(m_prefix + "\"" + name + "\": " + type + "\n"); + m_prefix += INDENT; + m_scope.push(type == '{' ? '}' : ']'); + return *this; + } + V3OutJsonFile& begin(char type = '{') { + comma(); + puts(m_prefix + type + "\n"); + m_prefix += INDENT; + m_scope.push(type == '{' ? '}' : ']'); + return *this; + } + + V3OutJsonFile& put(const std::string& name, const std::string& value) { + comma(); + puts(m_prefix + "\"" + name + "\": \"" + value + "\""); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& name, bool value) { + comma(); + puts(m_prefix + "\"" + name + "\": " + (value ? "true" : "false")); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& name, int value) { + comma(); + puts(m_prefix + "\"" + name + "\": " + std::to_string(value)); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& value) { + comma(); + puts(m_prefix + "\"" + value + "\""); + m_empty = false; + return *this; + } + V3OutJsonFile& put(bool value) { + comma(); + puts(m_prefix + (value ? "true" : "false")); + m_empty = false; + return *this; + } + V3OutJsonFile& put(int value) { + comma(); + puts(m_prefix + std::to_string(value)); + m_empty = false; + return *this; + } + + template + V3OutJsonFile& putList(const std::string& name, const T& list) { + if (list.empty()) return *this; + begin(name, '['); + for (auto it = list.begin(); it != list.end(); ++it) put(*it); + return end(); + } + + V3OutJsonFile& end() { + assert(m_prefix.length() >= strlen(INDENT)); + m_prefix.erase(m_prefix.end() - strlen(INDENT), m_prefix.end()); + assert(!m_scope.empty()); + puts("\n" + m_prefix + m_scope.top()); + m_scope.pop(); + return *this; + } + + V3OutJsonFile& operator+=(V3OutJsonFile& cursor) { + // Meaningless syntax sugar, at least for now + return *this; + } + +private: + void comma() { + if (!m_empty) puts(",\n"); + m_empty = true; + } +}; + +class V3OutMkFile final : public V3OutFile { +public: + explicit V3OutMkFile(const string& filename) + : V3OutFile{filename, V3OutFormatter::LA_MK} {} + ~V3OutMkFile() override = default; + virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } + // No automatic indentation yet. + void puts(const char* strg) { putsNoTracking(strg); } + void puts(const string& strg) { putsNoTracking(strg); } +}; + class V3OutScFile final : public V3OutCFile { public: explicit V3OutScFile(const string& filename) @@ -317,17 +425,6 @@ public: virtual void putsHeader() { puts("\n"); } }; -class V3OutMkFile final : public V3OutFile { -public: - explicit V3OutMkFile(const string& filename) - : V3OutFile{filename, V3OutFormatter::LA_MK} {} - ~V3OutMkFile() override = default; - virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } - // No automatic indentation yet. - void puts(const char* strg) { putsNoTracking(strg); } - void puts(const string& strg) { putsNoTracking(strg); } -}; - //============================================================================ // VIdProtect: Hash identifier names in output files to protect them From 100e3d7702b8dcf545fb8c3cf9a040761390b8d2 Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Sat, 10 May 2025 19:01:15 +0900 Subject: [PATCH 050/211] Fix const-bit-op-tree with single-bit masks (#5993) (#5998) --- src/V3Const.cpp | 2 ++ test_regress/t/t_opt_const.v | 28 ++++++++++++++++++++++++++++ test_regress/t/t_opt_const_dfg.py | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 04ea33435..3ce2f1087 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -125,6 +125,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { void updateBitRange(const AstShiftR* shiftp) { m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt(); } + void limitBitRangeToLsb() { m_msb = std::min(m_msb, m_lsb); } int wordIdx() const { return m_wordIdx; } void wordIdx(int i) { m_wordIdx = i; } bool polarity() const { return m_polarity; } @@ -538,6 +539,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { Restorer restorer{*this}; incrOps(nodep, __LINE__); iterateConst(nodep->rhsp()); + if (m_leafp) m_leafp->limitBitRangeToLsb(); CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp()); restorer.disableRestore(); // Now all checks passed } else if (nodep->type() == m_rootp->type()) { // And, Or, Xor diff --git a/test_regress/t/t_opt_const.v b/test_regress/t/t_opt_const.v index 5abf755f1..970774fe3 100644 --- a/test_regress/t/t_opt_const.v +++ b/test_regress/t/t_opt_const.v @@ -153,6 +153,7 @@ module Test(/*AUTOARG*/ bug4857 i_bug4857(.clk(clk), .in(d), .out(bug4857_out)); bug4864 i_bug4864(.clk(clk), .in(d), .out(bug4864_out)); bug5186 i_bug5186(.clk(clk), .in(d), .out(bug5186_out)); + bug5993 i_bug5993(.clk(clk), .in(d[10])); endmodule @@ -566,3 +567,30 @@ module bug5186(input wire clk, input wire [31:0] in, output out); result <= bad; assign out = result; endmodule + + +// See issue #5993 +// "in4[18]" is just one bit width, so " >> 8'd1" shifts out the bit. +// BitOpTree ignored implicit "& 1". It caused the bug" +module bug5993(input wire clk, input wire in); + + reg in3; + reg [23:16] in4; + + task automatic checkd(logic gotv, logic expv); + if ((gotv) !== (expv)) begin + $write("%%Error: got=%0d exp=%0d\n", gotv, expv); + $stop; + end + endtask + + // verilator lint_off WIDTH + wire wire_2 = in3 ? {4{14'b010111101}} : (in4[18] >> 8'b1); + // verilator lint_on WIDTH + + always @(posedge clk) begin + in3 <= '0; + in4 <= in ? 8'b00111__0__10 : 8'b00111__1__10; + checkd(wire_2, 1'b0); + end +endmodule diff --git a/test_regress/t/t_opt_const_dfg.py b/test_regress/t/t_opt_const_dfg.py index ffdca2c4f..6fd2ea9bc 100755 --- a/test_regress/t/t_opt_const_dfg.py +++ b/test_regress/t/t_opt_const_dfg.py @@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 39) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 43) test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1) test.passes() From d0424862f9534bff0046f9227d10ba209d615dfe Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 May 2025 13:22:26 -0400 Subject: [PATCH 051/211] Commentary: Changes update --- Changes | 5 +++++ Makefile.in | 5 +++++ docs/guide/exe_verilator.rst | 13 ++++++++----- nodist/log_changes | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Changes b/Changes index 6404647b4..ccf94390d 100644 --- a/Changes +++ b/Changes @@ -13,14 +13,19 @@ Verilator 5.037 devel **Other:** +* Support constrained random for associative arrays (#5985) (#5986). [Yilou Wang] * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). * Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore] +* Fix dependencies from different hierarchical schedules (#5954). [Bartłomiej Chmiel, Antmicro Ltd.] * Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972) (#5983). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] * Fix streaming to/from packed arrays (#5976). [Geza Lore] +* Fix inconsistent assignment error with split-var (#5984) (#5988). [Yutetsu TAKATSUKASA] +* Fix AstAssignW conversion (#5991) (#5992). [Ryszard Rozak, Antmicro Ltd.] +* Fix const-bit-op-tree with single-bit masks (#5993) (#5998). [Yutetsu TAKATSUKASA] Verilator 5.036 2025-04-27 diff --git a/Makefile.in b/Makefile.in index 5db547897..88a239a6f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -224,6 +224,11 @@ TAGS: $(TAGFILES) doxygen: $(MAKE) -C docs doxygen +.PHONY: spelling +spelling: + $(MAKE) -C docs spelling + + ###################################################################### # Install diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index fad7ec37e..ef956e3fd 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -607,6 +607,10 @@ Summary: .. option:: -fno-gate + Rarely needed. Do not apply the gate-level wire optimizations. Using + this is not recommended as may cause additional warnings and ordering + issues. + .. option:: -fno-inline .. option:: -fno-inline-funcs @@ -1273,8 +1277,8 @@ Summary: .. option:: --public - This is only for historical debugging use and using it may result in - mis-simulation of generated clocks. + Rarely needed. This is only for historical debugging use and using it + may result in mis-simulation of generated clocks. Declares all signals and modules public. This will turn off signal optimizations as if all signals had a :option:`/*verilator&32;public*/` @@ -1564,7 +1568,6 @@ Summary: This is not needed with standard designs with only one top. See also :option:`MULTITOP` warning. - .. option:: --trace Deprecated; use :vlopt:`--trace-fst`, :vlopt:`--trace-saif` or @@ -1720,8 +1723,8 @@ Summary: .. option:: --valgrind - Run Verilator under `Valgrind `_. The command may be - changed with :option:`VERILATOR_VALGRIND`. + Rarely needed. Run Verilator under `Valgrind `_. + The command may be changed with :option:`VERILATOR_VALGRIND`. .. option:: --no-verilate diff --git a/nodist/log_changes b/nodist/log_changes index 220bcfe8b..77874edc7 100755 --- a/nodist/log_changes +++ b/nodist/log_changes @@ -90,7 +90,7 @@ def process(): print() print("You may now want to clean up spelling, and commit:") - print(" (cd docs ; make spelling | grep -vi 'writing output')") + print(" (make spelling | grep -vi 'writing output')") print(" git ci -am 'Commentary: Changes update'") print() From d9dcde60a6581ab80b1d55bbb43a0b4214f5a5c3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 May 2025 13:29:30 -0400 Subject: [PATCH 052/211] Fix duplicate error first-lines, and some internal V3Error cleanups --- src/V3Error.cpp | 36 +++++++----- src/V3Error.h | 7 ++- src/V3LinkLevel.cpp | 6 +- test_regress/t/t_generate_fatal_bad.out | 77 ------------------------- test_regress/t/t_pp_line_bad.out | 6 -- 5 files changed, 29 insertions(+), 103 deletions(-) diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 2d0d30688..1acd8e601 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -49,6 +49,14 @@ V3ErrorCode::V3ErrorCode(const char* msgp) { m_e = V3ErrorCode::EC_ERROR; } +string V3ErrorCode::url() const { + if (m_e < V3ErrorCode::EC_FIRST_NAMED) { + return "https://verilator.org/verilator_doc.html"s + "?v=" + PACKAGE_VERSION_NUMBER_STRING; + } else { + return "https://verilator.org/warn/"s + ascii() + "?v=" + PACKAGE_VERSION_NUMBER_STRING; + } +} + //###################################################################### // V3ErrorGuarded class functions // @@ -128,14 +136,18 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) && (!debug() || debug() < 3 || m_errorCode.defaultsOff())) return; string msg = msgPrefix() + sstr.str(); + // If suppressed print only first line to reduce verbosity - if (m_errorSuppressed) { - string::size_type pos; - if ((pos = msg.find('\n')) != string::npos) { - msg.erase(pos, msg.length() - pos); - msg += "..."; - } + string firstLine = msg; + string::size_type pos; + if ((pos = firstLine.find('\n')) != string::npos) { + firstLine.erase(pos, firstLine.length() - pos); + firstLine += "..."; } + if (m_errorSuppressed) msg = firstLine; + // Suppress duplicate messages + if (!m_messages.insert(firstLine).second) return; + string msg_additional; { string::size_type pos; @@ -152,8 +164,6 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) while ((pos = msg_additional.find("\n\n")) != string::npos) msg_additional.erase(pos + 1, 1); } - // Suppress duplicate messages - if (!m_messages.insert(msg).second) return; if (!extra.empty() && !m_errorSuppressed) { const string extraMsg = warnMore() + extra + "\n"; const size_t pos = msg.find('\n'); @@ -175,13 +185,9 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) if (m_errorCode != V3ErrorCode::EC_FATALMANY // Not verbose on final too-many-errors error && !m_describedEachWarn[m_errorCode]) { m_describedEachWarn[m_errorCode] = true; - const string docUrl = "https://verilator.org/verilator_doc.html"s - + "?v=" + PACKAGE_VERSION_NUMBER_STRING; - const string warnUrl = "https://verilator.org/warn/"s + m_errorCode.ascii() - + "?v=" + PACKAGE_VERSION_NUMBER_STRING; if (m_errorCode >= V3ErrorCode::EC_FIRST_NAMED) { std::cerr << warnMore() << "... For " << (anError ? "error" : "warning") - << " description see " << warnUrl << endl; + << " description see " << m_errorCode.url() << endl; } else if (m_errCount >= 1 && (m_errorCode == V3ErrorCode::EC_FATAL || m_errorCode == V3ErrorCode::EC_FATALMANY @@ -194,7 +200,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) << endl; } else if (!m_tellManual) { m_tellManual = true; - std::cerr << warnMore() << "... See the manual at " << docUrl + std::cerr << warnMore() << "... See the manual at " << m_errorCode.url() << " for more assistance." << endl; } if (!m_pretendError[m_errorCode] && !m_errorCode.hardError()) { @@ -202,7 +208,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) << m_errorCode.ascii() << " */\" and lint_on around source to disable this message." << endl; if (m_errorCode.dangerous()) { - std::cerr << warnMore() << "*** See " << warnUrl + std::cerr << warnMore() << "*** See " << m_errorCode.url() << " before disabling this,\n"; std::cerr << warnMore() << "else you may end up with different sim results." << endl; diff --git a/src/V3Error.h b/src/V3Error.h index 591128e8e..fdae76e3a 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -34,6 +34,9 @@ #include #include +class V3Error; +class FileLine; + //###################################################################### class V3ErrorCode final { @@ -289,7 +292,7 @@ public: } return false; } - + string url() const; static bool unusedMsg(const char* msgp) { return 0 == VL_STRCASECMP(msgp, "UNUSED"); } }; constexpr bool operator==(const V3ErrorCode& lhs, const V3ErrorCode& rhs) { @@ -302,7 +305,6 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) { } // ###################################################################### -class V3Error; class V3ErrorGuarded final { // Should only be used by V3ErrorGuarded::m_mutex is already locked @@ -409,6 +411,7 @@ public: }; // ###################################################################### + class V3Error final { // Base class for any object that wants debugging and error reporting // CONSTRUCTORS diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index e90eda0a0..fce12adaf 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -53,11 +53,11 @@ void V3LinkLevel::modSortByLevel() { if (tops.size() >= 2) { const AstNode* const secp = tops[1]; // Complain about second one, as first often intended if (!secp->fileline()->warnIsOff(V3ErrorCode::MULTITOP)) { - auto warnTopModules = [](const std::string& warnMore, ModVec tops) + auto warnTopModules = [](const AstNode* const secp, ModVec tops) VL_REQUIRES(V3Error::s().m_mutex) -> std::string { std::stringstream ss; for (AstNode* alsop : tops) { - ss << warnMore << "... Top module " << alsop->prettyNameQ() << endl + ss << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl << alsop->warnContextSecondary(); } return ss.str(); @@ -69,7 +69,7 @@ void V3LinkLevel::modSortByLevel() { "--top-module to select top." << V3Error::s().warnContextNone() << V3Error::warnAdditionalInfo() - << warnTopModules(secp->warnMore(), tops)); + << warnTopModules(secp, tops)); } } diff --git a/test_regress/t/t_generate_fatal_bad.out b/test_regress/t/t_generate_fatal_bad.out index d657d50bb..aa62b75d5 100644 --- a/test_regress/t/t_generate_fatal_bad.out +++ b/test_regress/t/t_generate_fatal_bad.out @@ -9,81 +9,4 @@ 13 | localparam integer BAZ = get_baz(BAR); | ^~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.genloop[1].foo_inst' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = ?32?h1 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.gen_l1[2].gen_l2[0].foo_inst2' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h2 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.gen_l1[2].gen_l2[1].foo_inst2' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h4 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.gen_l1[3].gen_l2[0].foo_inst2' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h3 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.gen_l1[3].gen_l2[1].foo_inst2' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h5 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.cond_true.foo_inst3' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = ?32?h6 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.genblk4.foo_inst4' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = ?32?h7 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.nested_loop[8].foo2_inst.foo2_loop[0].foo_in_foo2_inst' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h8 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.nested_loop[8].foo2_inst.foo2_loop[1].foo_in_foo2_inst' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'h9 - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.nested_loop[10].foo2_inst.foo2_loop[0].foo_in_foo2_inst' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'ha - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ -%Error: t/t_generate_fatal_bad.v:13:29: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_baz' - : ... note: In instance 't.nested_loop[10].foo2_inst.foo2_loop[1].foo_in_foo2_inst' - t/t_generate_fatal_bad.v:9:4: ... Location of non-constant STOP: $stop executed during function constification; maybe indicates assertion firing - t/t_generate_fatal_bad.v:13:29: ... Called from 'get_baz()' with parameters: - bar = 32'hb - 13 | localparam integer BAZ = get_baz(BAR); - | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_pp_line_bad.out b/test_regress/t/t_pp_line_bad.out index d2f6c2291..adce0e39f 100644 --- a/test_regress/t/t_pp_line_bad.out +++ b/test_regress/t/t_pp_line_bad.out @@ -2,27 +2,21 @@ 8 | `line 100 | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_pp_line_bad.v:8:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:9:1: `line was not properly formed with '`line number "filename" level' 9 | `line 200 somefile | ^ -%Error: t/t_pp_line_bad.v:9:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:10:1: `line was not properly formed with '`line number "filename" level' 10 | `line 300 "somefile 1 | ^ -%Error: t/t_pp_line_bad.v:10:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:11:1: `line was not properly formed with '`line number "filename" level' 11 | `line 400 "some file" | ^ -%Error: t/t_pp_line_bad.v:11:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:12:1: `line was not properly formed with '`line number "filename" level' 12 | `line 500 "somefile" 3 | ^ -%Error: t/t_pp_line_bad.v:12:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:13:1: `line was not properly formed with '`line number "filename" level' 13 | `line 600 "some file" 3 | ^ -%Error: t/t_pp_line_bad.v:13:1: `line was not properly formed with '`line number "filename" level' %Error: t/t_pp_line_bad.v:7:1: Define or directive not defined: '`line' 7 | `line | ^~~~~ From 0f528d136d0e9ba10867154e1ed49eb3cf7eed25 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 May 2025 13:59:56 -0400 Subject: [PATCH 053/211] Fix arithmetic right-shift by constants over 32 bits (#5994). --- Changes | 1 + src/V3Number.cpp | 24 +++++++++--------------- test_regress/t/t_math_shiftrs2.py | 18 ++++++++++++++++++ test_regress/t/t_math_shiftrs2.v | 22 ++++++++++++++++++++++ 4 files changed, 50 insertions(+), 15 deletions(-) create mode 100755 test_regress/t/t_math_shiftrs2.py create mode 100644 test_regress/t/t_math_shiftrs2.v diff --git a/Changes b/Changes index ccf94390d..068b67749 100644 --- a/Changes +++ b/Changes @@ -26,6 +26,7 @@ Verilator 5.037 devel * Fix inconsistent assignment error with split-var (#5984) (#5988). [Yutetsu TAKATSUKASA] * Fix AstAssignW conversion (#5991) (#5992). [Ryszard Rozak, Antmicro Ltd.] * Fix const-bit-op-tree with single-bit masks (#5993) (#5998). [Yutetsu TAKATSUKASA] +* Fix arithmetic right-shift by constants over 32 bits (#5994). Verilator 5.036 2025-04-27 diff --git a/src/V3Number.cpp b/src/V3Number.cpp index f0280076c..13a1984fb 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1853,23 +1853,17 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t NUM_ASSERT_LOGIC_ARGS2(lhs, rhs); if (rhs.isFourState()) return setAllBitsX(); setZero(); - for (int bit = 32; bit < rhs.width(); ++bit) { - for (int sbit = 0; sbit < width(); ++sbit) { - setBit(sbit, lhs.bitIs(lbits - 1)); // 0/1/X/Z - } - if (rhs.bitIs1(lbits - 1)) setAllBits1(); // -1 else 0 - return *this; // shift of over 2^32 must be -1/0 - } - const uint32_t rhsval = rhs.toUInt(); - if (rhsval < static_cast(lhs.width())) { - for (int bit = 0; bit < width(); ++bit) { - setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits)); - } - } else { - for (int bit = 0; bit < width(); ++bit) { - setBit(bit, lhs.bitIs(lbits - 1)); // 0/1/X/Z + const bool overflow = rhs.width() > 32 && !rhs.isBitsZero(rhs.width() - 1, 32); + if (!overflow) { + const uint32_t rhsval = rhs.toUInt(); + if (rhsval < static_cast(lhs.width())) { + for (int bit = 0; bit < width(); ++bit) { + setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits)); + } + return *this; } } + for (int bit = 0; bit < width(); ++bit) setBit(bit, lhs.bitIs(lbits - 1)); // '0/'1/'x/'z return *this; } diff --git a/test_regress/t/t_math_shiftrs2.py b/test_regress/t/t_math_shiftrs2.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_math_shiftrs2.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']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_shiftrs2.v b/test_regress/t/t_math_shiftrs2.v new file mode 100644 index 000000000..541902b6b --- /dev/null +++ b/test_regress/t/t_math_shiftrs2.v @@ -0,0 +1,22 @@ +// 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 + +`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); + +module top(out35); + output wire [2:0] out35; + wire signed [2:0] wire_4; + assign wire_4 = 3'b011; + assign out35 = (wire_4 >>> 36'hffff_ffff_f); + + initial begin + #10; + `checkd(out35, '0); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 680236b03ee23d037834f00a78f9e52e52724f9a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 May 2025 16:20:12 -0400 Subject: [PATCH 054/211] Internals: Redo post-error additional information to be part of error calls. --- src/V3Error.cpp | 8 +- src/V3Error.h | 23 ++---- src/V3FileLine.cpp | 4 - src/V3FileLine.h | 2 - src/V3Graph.cpp | 13 +-- src/V3Graph.h | 6 +- src/V3GraphAcyc.cpp | 7 +- src/V3GraphAlg.cpp | 17 ++-- src/V3LinkCells.cpp | 4 +- src/V3LinkDot.cpp | 30 ++++--- src/V3Options.cpp | 27 ++++--- src/V3Options.h | 2 +- src/V3SchedAcyclic.cpp | 81 +++++++++++-------- src/V3SymTable.h | 6 +- src/V3Width.cpp | 9 ++- test_regress/t/t_gen_nonconst_bad.out | 4 +- test_regress/t/t_interface_mismodport_bad.out | 2 +- 17 files changed, 128 insertions(+), 117 deletions(-) diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 1acd8e601..dcb00df3d 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -122,6 +122,13 @@ void V3ErrorGuarded::suppressThisWarning() VL_REQUIRES(m_mutex) { errorSuppressed(true); } +void V3ErrorGuarded::v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) { + m_errorStr.str(""); + m_errorCode = code; + m_errorContexted = false; + m_errorSuppressed = false; +} + // cppcheck-has-bug-suppress constParameter void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_REQUIRES(m_mutex) { @@ -303,7 +310,6 @@ std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* f v3errorPrep(code) << file << ":" << std::dec << line << ": "; return v3errorStr(); } -std::ostringstream& V3Error::v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); } void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) { s().v3errorEnd(sstr, extra); V3Error::s().m_mutex.unlock(); diff --git a/src/V3Error.h b/src/V3Error.h index fdae76e3a..8a5d669c1 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -325,7 +325,7 @@ private: = V3ErrorCode::EC_FATAL; // Error string being formed will abort bool m_errorSuppressed VL_GUARDED_BY(m_mutex) = false; // Error being formed should be suppressed - MessagesSet m_messages VL_GUARDED_BY(m_mutex); // What errors we've outputted + MessagesSet m_messages VL_GUARDED_BY(m_mutex); // Errors outputted, to remove dups ErrorExitCb m_errorExitCb VL_GUARDED_BY(m_mutex) = nullptr; // Callback when error occurs for dumping bool m_errorContexted VL_GUARDED_BY(m_mutex) = false; // Error being formed got context @@ -341,12 +341,7 @@ private: bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed - void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) { - m_errorStr.str(""); - m_errorCode = code; - m_errorContexted = false; - m_errorSuppressed = false; - } + void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex); std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; } void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex); @@ -363,9 +358,9 @@ public: bool isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex); void vlAbortOrExit() VL_REQUIRES(m_mutex); void errorContexted(bool flag) VL_REQUIRES(m_mutex) { m_errorContexted = flag; } - void incWarnings() VL_REQUIRES(m_mutex) { m_warnCount++; } + void incWarnings() VL_REQUIRES(m_mutex) { ++m_warnCount; } void incErrors() VL_REQUIRES(m_mutex) { - m_errCount++; + ++m_errCount; if (errorCount() == errorLimit()) { // Not >= as would otherwise recurse v3errorEnd( (v3errorPrep(V3ErrorCode::EC_FATALMANY), @@ -509,14 +504,6 @@ public: // When printing an error/warning, print prefix for multiline message static string warnMore() VL_REQUIRES(s().m_mutex) { return s().warnMore(); } - // This function should only be used when it is impossible to - // generate whole error message inside v3warn macros and it needs to be - // streamed directly to cerr. - // Use with caution as this function isn't MT_SAFE. - static string warnMoreStandalone() VL_EXCLUDES(s().m_mutex) VL_MT_UNSAFE { - const V3RecursiveLockGuard guard{s().m_mutex}; - return s().warnMore(); - } // This function marks place in error message from which point message // should be printed after information on the error code. // The post-processing is done in v3errorEnd function. @@ -532,7 +519,7 @@ public: static std::ostringstream& v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex); static std::ostringstream& v3errorPrepFileLine(V3ErrorCode code, const char* file, int line) VL_ACQUIRE(s().m_mutex); - static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex); + static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); } // static, but often overridden in classes. static void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_RELEASE(s().m_mutex); diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 1ff188e88..2247695c7 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -464,10 +464,6 @@ string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return V3Error::s().warnMore(); } }; -string FileLine::warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE { - const V3RecursiveLockGuard guard{V3Error::s().m_mutex}; - return warnOther(); -} string FileLine::source() const VL_MT_SAFE { if (VL_UNCOVERABLE(!m_contentp)) { // LCOV_EXCL_START diff --git a/src/V3FileLine.h b/src/V3FileLine.h index fe3a6d986..ead89fd63 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -301,7 +301,6 @@ public: void warnUnusedOff(bool flag); void warnStateFrom(const FileLine& from) { m_msgEnIdx = from.m_msgEnIdx; } void warnResetDefault() { warnStateFrom(defaultFileLine()); } - bool lastWarnWaived() const { return m_waive; } // Specific flag ACCESSORS/METHODS bool celldefineOn() const { return msgEn().test(V3ErrorCode::I_CELLDEFINE); } @@ -356,7 +355,6 @@ public: /// When building an error, prefix for printing secondary information /// from a different FileLine than the original error string warnOther() const VL_REQUIRES(V3Error::s().m_mutex); - string warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE; /// When building an error, current location in include etc /// If not used in a given error, automatically pasted at end of error string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) { diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index a1af41bb9..de1d19ca9 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -233,13 +233,16 @@ void V3Graph::clearColors() { //====================================================================== // Dumping -void V3Graph::loopsMessageCb(V3GraphVertex* vertexp) { - vertexp->v3fatalSrc("Loops detected in graph: " << vertexp); +void V3Graph::loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFuncp) { + vertexp->v3fatalSrc("Loops detected in graph: " << vertexp << "\n" + << reportLoops(edgeFuncp, vertexp)); } - -void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) { +string V3Graph::loopsVertexCb(V3GraphVertex* vertexp) { // Needed here as V3GraphVertex<< isn't defined until later in header - if (debug()) std::cerr << "-Info-Loop: " << cvtToHex(vertexp) << " " << vertexp << endl; + if (debug()) + return "-Info-Loop: "s + cvtToHex(vertexp) + ' ' + cvtToStr(vertexp) + '\n'; + else + return ""; } void V3Graph::dump(std::ostream& os) const { diff --git a/src/V3Graph.h b/src/V3Graph.h index 9b357ceff..222806bb8 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -430,7 +430,7 @@ public: /// Call loopsVertexCb on any one loop starting where specified /// Side-effect: changes user() - void reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) VL_MT_DISABLED; + string reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) VL_MT_DISABLED; /// Build a subgraph of all loops starting where specified /// Side-effect: changes user() @@ -484,8 +484,8 @@ public: parallelismReport(std::function vertexCost) VL_MT_DISABLED; // CALLBACKS - virtual void loopsMessageCb(V3GraphVertex* vertexp) VL_MT_DISABLED; - virtual void loopsVertexCb(V3GraphVertex* vertexp) VL_MT_DISABLED; + virtual void loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFuncp) VL_MT_DISABLED; + virtual string loopsVertexCb(V3GraphVertex* vertexp) VL_MT_DISABLED; }; //============================================================================ diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index cf3ab8b9c..5e615e1e0 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -343,10 +343,9 @@ void GraphAcyc::simplifyOut(GraphAcycVertex* avertexp) { V3GraphVertex* inVertexp = inEdgep->fromp(); if (inVertexp == avertexp) { if (debug()) v3error("Non-cutable vertex=" << avertexp); // LCOV_EXCL_LINE - v3error("Circular logic when ordering code (non-cutable edge loop)"); - m_origGraphp->reportLoops( - &V3GraphEdge::followNotCutable, - avertexp->origVertexp()); // calls OrderGraph::loopsVertexCb + v3error("Circular logic when ordering code (non-cutable edge loop)\n" + << m_origGraphp->reportLoops( // calls OrderGraph::loopsVertexCb + &V3GraphEdge::followNotCutable, avertexp->origVertexp())); // Things are unlikely to end well at this point, // but we'll try something to get to further errors... inEdgep->cutable(true); diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index bc0eff17f..56b6e7e1a 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -281,8 +282,7 @@ class GraphAlgRank final : GraphAlg<> { // If larger rank is found, assign it and loop back through // If we hit a back node make a list of all loops if (vertexp->user() == 1) { - m_graphp->reportLoops(m_edgeFuncp, vertexp); - m_graphp->loopsMessageCb(vertexp); + m_graphp->loopsMessageCb(vertexp, m_edgeFuncp); return; // LCOV_EXCL_LINE // gcc gprof bug misses this return } if (vertexp->rank() >= currentRank) return; // Already processed it @@ -313,6 +313,7 @@ void V3Graph::rank(V3EdgeFuncP edgeFuncp) { GraphAlgRank{this, edgeFuncp}; } class GraphAlgRLoops final : GraphAlg<> { std::vector m_callTrace; // List of everything we hit processing so far + std::vector m_msgs; // Output messages bool m_done = false; // Exit algorithm void main(V3GraphVertex* vertexp) { @@ -333,9 +334,8 @@ class GraphAlgRLoops final : GraphAlg<> { m_callTrace[currentRank++] = vertexp; if (vertexp->user() == 1) { - for (unsigned i = 0; i < currentRank; i++) { // - m_graphp->loopsVertexCb(m_callTrace[i]); - } + for (unsigned i = 0; i < currentRank; i++) + m_msgs.emplace_back(m_graphp->loopsVertexCb(m_callTrace[i])); m_done = true; return; } @@ -353,10 +353,13 @@ public: main(vertexp); } ~GraphAlgRLoops() = default; + string message() const { + return std::accumulate(m_msgs.begin(), m_msgs.end(), std::string{""}); + } }; -void V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) { - GraphAlgRLoops{this, edgeFuncp, vertexp}; +string V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) { + return GraphAlgRLoops{this, edgeFuncp, vertexp}.message(); } //###################################################################### diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index ed24b42d7..e36611257 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -43,7 +43,7 @@ class LinkCellsGraph final : public V3Graph { public: LinkCellsGraph() = default; ~LinkCellsGraph() override = default; - void loopsMessageCb(V3GraphVertex* vertexp) override; + void loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFuncp) override; }; class LinkCellsVertex final : public V3GraphVertex { @@ -73,7 +73,7 @@ public: string name() const override VL_MT_STABLE { return "*LIBRARY*"; } }; -void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { +void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFuncp) { if (const LinkCellsVertex* const vvertexp = vertexp->cast()) { vvertexp->modp()->v3warn(E_UNSUPPORTED, "Unsupported: Recursive multiple modules (module instantiates " diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 5ace4d040..b221d8a09 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3422,11 +3422,11 @@ class LinkDotResolveVisitor final : public VNVisitor { << (!baddot.empty() ? AstNode::prettyNameQ(baddot) : nodep->prettyNameQ()) << " in dotted " << expectWhat << ": '" - << m_ds.m_dotText + "." + nodep->prettyName() << "'"); - if (okSymp) { - okSymp->cellErrorScopes(nodep, - AstNode::prettyName(m_ds.m_dotText)); - } + << m_ds.m_dotText + "." + nodep->prettyName() << "'\n" + << nodep->warnContextPrimary() + << (okSymp ? okSymp->cellErrorScopes( + nodep, AstNode::prettyName(m_ds.m_dotText)) + : "")); } m_ds.m_dotErr = true; } @@ -3611,8 +3611,9 @@ class LinkDotResolveVisitor final : public VNVisitor { if (!nodep->varp()) { nodep->v3error("Can't find definition of " << AstNode::prettyNameQ(baddot) << " in dotted signal: '" - << nodep->dotted() + "." + nodep->prettyName() << "'"); - okSymp->cellErrorScopes(nodep); + << nodep->dotted() + "." + nodep->prettyName() << "'\n" + << nodep->warnContextPrimary() + << okSymp->cellErrorScopes(nodep)); return; } // V3Inst may have expanded arrays of interfaces to @@ -3636,8 +3637,9 @@ class LinkDotResolveVisitor final : public VNVisitor { if (!vscp) { nodep->v3error("Can't find varpin scope of " << AstNode::prettyNameQ(baddot) << " in dotted signal: '" - << nodep->dotted() + "." + nodep->prettyName() << "'"); - okSymp->cellErrorScopes(nodep); + << nodep->dotted() + "." + nodep->prettyName() << "'\n" + << nodep->warnContextPrimary() + << okSymp->cellErrorScopes(nodep)); } else { while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal UINFO(7, indent() << "Resolved pre-alias " << vscp @@ -3844,10 +3846,11 @@ class LinkDotResolveVisitor final : public VNVisitor { dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, inl, baddot, okSymp, true); if (!dotSymp) { - okSymp->cellErrorScopes(nodep); nodep->v3fatalSrc("Couldn't resolve inlined scope " << AstNode::prettyNameQ(baddot) - << " in: " << nodep->inlinedDots()); + << " in: " << nodep->inlinedDots() << '\n' + << nodep->warnContextPrimary() + << okSymp->cellErrorScopes(nodep)); } } dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot, @@ -3974,8 +3977,9 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->v3error("Can't find definition of " << AstNode::prettyNameQ(baddot) << " in dotted task/function: '" << nodep->dotted() + "." + nodep->prettyName() << "'\n" - << (suggest.empty() ? "" : nodep->warnMore() + suggest)); - okSymp->cellErrorScopes(nodep); + << (suggest.empty() ? "" : nodep->warnMore() + suggest) << '\n' + << nodep->warnContextPrimary() + << okSymp->cellErrorScopes(nodep)); } } } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 25f612395..de2d030a9 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -595,40 +595,41 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la // Warn and return not found if (errmsg != "") { - fl->v3error(errmsg + "'"s + filename + "'"s); - filePathLookedMsg(fl, filename); + fl->v3error(errmsg << "'"s << filename << "'\n"s << fl->warnContextPrimary() + << V3Error::warnAdditionalInfo() << filePathLookedMsg(fl, filename)); } return ""; } -void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) { +string V3Options::filePathLookedMsg(FileLine* fl, const string& modname) { static bool shown_notfound_msg = false; + std::ostringstream ss; if (modname.find("__Vhsh") != string::npos) { - std::cerr << V3Error::warnMoreStandalone() - << "... Note: Name is longer than 127 characters; automatic" - << " file lookup may have failed due to OS filename length limits.\n"; - std::cerr << V3Error::warnMoreStandalone() - << "... Suggest putting filename with this module/package" - << " onto command line instead.\n"; + ss << V3Error::warnMore() << "... Note: Name is longer than 127 characters; automatic" + << " file lookup may have failed due to OS filename length limits.\n"; + ss << V3Error::warnMore() << "... Suggest putting filename with this module/package" + << " onto command line instead.\n"; } else if (!shown_notfound_msg) { shown_notfound_msg = true; if (m_impp->m_incDirUsers.empty()) { - fl->v3error("This may be because there's no search path specified with -I."); + ss << V3Error::warnMore() + << "... This may be because there's no search path specified with -I.\n"; } - std::cerr << V3Error::warnMoreStandalone() << "... Looked in:" << endl; + ss << V3Error::warnMore() << "... Looked in:\n"; for (const string& dir : m_impp->m_incDirUsers) { for (const string& ext : m_impp->m_libExtVs) { const string fn = V3Os::filenameJoin(dir, modname + ext); - std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl; + ss << V3Error::warnMore() << " " << fn << "\n"; } } for (const string& dir : m_impp->m_incDirFallbacks) { for (const string& ext : m_impp->m_libExtVs) { const string fn = V3Os::filenameJoin(dir, modname + ext); - std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl; + ss << V3Error::warnMore() << " " << fn << "\n"; } } } + return ss.str(); } //! Determine what language is associated with a filename diff --git a/src/V3Options.h b/src/V3Options.h index 75612db10..9ab1b61e3 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -779,7 +779,7 @@ public: string fileExists(const string& filename); string filePath(FileLine* fl, const string& modname, const string& lastpath, const string& errmsg); - void filePathLookedMsg(FileLine* fl, const string& modname); + string filePathLookedMsg(FileLine* fl, const string& modname); V3LangCode fileLanguage(const string& filename); static bool fileStatNormal(const string& filename); diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index 54d275b28..46c91aa9e 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -100,17 +100,16 @@ public: }; class Graph final : public V3Graph { - void loopsVertexCb(V3GraphVertex* vtxp) override { - // TODO: 'typeName' is an internal thing. This should be more human readable. + string loopsVertexCb(V3GraphVertex* vtxp) override { if (SchedAcyclicLogicVertex* const lvtxp = vtxp->cast()) { AstNode* const logicp = lvtxp->logicp(); - std::cerr << logicp->fileline()->warnOtherStandalone() - << " Example path: " << logicp->typeName() << endl; + return logicp->fileline()->warnOther() + + " Example path: " + logicp->prettyTypeName() + "\n"; } else { SchedAcyclicVarVertex* const vvtxp = vtxp->as(); AstVarScope* const vscp = vvtxp->vscp(); - std::cerr << vscp->fileline()->warnOtherStandalone() - << " Example path: " << vscp->prettyName() << endl; + return vscp->fileline()->warnOther() + " Example path: " + vscp->prettyName() + + "\n"; } } }; @@ -268,7 +267,8 @@ void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector& candidates } // Find all variables in a loop (SCC) that are candidates for splitting to break loops. -void reportLoopVars(Graph* graphp, SchedAcyclicVarVertex* vvtxp) { +std::string reportLoopVars(FileLine* warnFl, Graph* graphp, SchedAcyclicVarVertex* vvtxp) { + std::ostringstream ss; // Vector of variables in UNOPTFLAT loop that are candidates for splitting. std::vector candidates; { @@ -281,47 +281,51 @@ void reportLoopVars(Graph* graphp, SchedAcyclicVarVertex* vvtxp) { } // Possible we only have candidates the user cannot do anything about, so don't bother them. - if (candidates.empty()) return; + if (candidates.empty()) return ""; // There may be a very large number of candidates, so only report up to 10 of the "most // important" signals. unsigned splittable = 0; - const auto reportFirst10 = [&](std::function less) { + const auto reportFirst10 + = [&](std::function less) -> string { std::stable_sort(candidates.begin(), candidates.end(), less); + std::ostringstream ss2; for (size_t i = 0; i < 10; i++) { if (i == candidates.size()) break; const Candidate& candidate = candidates[i]; AstVar* const varp = candidate.first->varp(); - std::cerr << V3Error::warnMoreStandalone() << " " << varp->fileline() << " " - << varp->prettyName() << ", width " << std::dec << varp->width() - << ", circular fanout " << candidate.second; + + ss2 << V3Error::warnMore() << " " << varp->fileline() << ' ' << varp->prettyName() + << ", width " << std::dec << varp->width() << ", circular fanout " + << candidate.second; if (V3SplitVar::canSplitVar(varp)) { - std::cerr << ", can split_var"; + ss2 << ", can split_var"; ++splittable; } - std::cerr << '\n'; + ss2 << '\n'; } + return ss2.str(); }; // Widest variables - std::cerr << V3Error::warnMoreStandalone() << "... Widest variables candidate to splitting:\n"; - reportFirst10([](const Candidate& a, const Candidate& b) { - return a.first->varp()->width() > b.first->varp()->width(); - }); + ss << V3Error::warnMore() << "... Widest variables candidate to splitting:\n" + << reportFirst10([](const Candidate& a, const Candidate& b) { + return a.first->varp()->width() > b.first->varp()->width(); + }); // Highest fanout - std::cerr << V3Error::warnMoreStandalone() << "... Candidates with the highest fanout:\n"; - reportFirst10([](const Candidate& a, const Candidate& b) { // - return a.second > b.second; - }); + ss << V3Error::warnMore() << "... Candidates with the highest fanout:\n" + << reportFirst10([](const Candidate& a, const Candidate& b) { // + return a.second > b.second; + }); if (splittable) { - std::cerr << V3Error::warnMoreStandalone() - << "... Suggest add /*verilator split_var*/ or /*verilator " - "isolate_assignments*/ to appropriate variables above." - << std::endl; + ss << V3Error::warnMore() + << "... Suggest add /*verilator split_var*/ or /*verilator " + "isolate_assignments*/ to appropriate variables above.\n"; } V3Stats::addStat("Scheduling, split_var, candidates", splittable); + return ss.str(); } void reportCycles(Graph* graphp, const std::vector& cutVertices) { @@ -330,17 +334,26 @@ void reportCycles(Graph* graphp, const std::vector& cutV FileLine* const flp = vscp->fileline(); // First v3warn not inside warnIsOff so we can see the suppressions with --debug - vscp->v3warn(UNOPTFLAT, "Signal unoptimizable: Circular combinational logic: " - << vscp->prettyNameQ()); - if (!flp->warnIsOff(V3ErrorCode::UNOPTFLAT) && !flp->lastWarnWaived()) { + if (flp->warnIsOff(V3ErrorCode::UNOPTFLAT)) { + // First v3warn not inside warnIsOff so we can see the suppressions with --debug + vscp->v3warn(UNOPTFLAT, "Signal unoptimizable: Circular combinational logic: " + << vscp->prettyNameQ()); + } else { + vscp->v3warn(UNOPTFLAT, + "Signal unoptimizable: Circular combinational logic: " + << vscp->prettyNameQ() << '\n' + << vscp->warnContextPrimary() + << V3Error::warnAdditionalInfo() + // Calls Graph::loopsVertexCb + << graphp->reportLoops(&V3GraphEdge::followAlwaysTrue, vvtxp) + // Report candidate variables for splitting + << (v3Global.opt.reportUnoptflat() + ? reportLoopVars(vscp->fileline(), graphp, vvtxp) + : "")); // Complain just once flp->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); - // Calls Graph::loopsVertexCb - graphp->reportLoops(&V3GraphEdge::followAlwaysTrue, vvtxp); + // Create a subgraph for the UNOPTFLAT loop if (v3Global.opt.reportUnoptflat()) { - // Report candidate variables for splitting - reportLoopVars(graphp, vvtxp); - // Create a subgraph for the UNOPTFLAT loop V3Graph loopGraph; graphp->subtreeLoops(&V3GraphEdge::followAlwaysTrue, vvtxp, &loopGraph); loopGraph.dumpDotFilePrefixedAlways("unoptflat"); diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 84cced2cd..20166c97e 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -256,7 +256,7 @@ public: } } } - void cellErrorScopes(AstNode* lookp, string prettyName = "") { + string cellErrorScopes(AstNode* lookp, string prettyName = "") { if (prettyName == "") prettyName = lookp->prettyName(); string scopes; for (IdNameMap::iterator it = m_idNameMap.begin(); it != m_idNameMap.end(); ++it) { @@ -267,9 +267,9 @@ public: } } if (scopes == "") scopes = ""; - std::cerr << V3Error::warnMoreStandalone() << "... Known scopes under '" << prettyName - << "': " << scopes << endl; if (debug()) dumpSelf(std::cerr, " KnownScope: ", 1); + return V3Error::warnMore() + "... Known scopes under '" + prettyName + "': " + scopes + + '\n'; } }; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8b9383f61..9f7e85728 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5876,9 +5876,12 @@ class WidthVisitor final : public VNVisitor { // We've resolved parameters and hit a module that we couldn't resolve. It's // finally time to report it. // Note only here in V3Width as this is first visitor after V3Dead. - nodep->modNameFileline()->v3error("Cannot find file containing module: '" - << nodep->modName() << "'"); - v3Global.opt.filePathLookedMsg(nodep->modNameFileline(), nodep->modName()); + nodep->modNameFileline()->v3error( + "Cannot find file containing module: '" + << nodep->modName() << "'\n" + << nodep->modNameFileline()->warnContextPrimary() + << V3Error::warnAdditionalInfo() + << v3Global.opt.filePathLookedMsg(nodep->modNameFileline(), nodep->modName())); } if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p()); userIterateAndNext(nodep->pinsp(), nullptr); diff --git a/test_regress/t/t_gen_nonconst_bad.out b/test_regress/t/t_gen_nonconst_bad.out index 5a50edbcd..9c7aa3c0c 100644 --- a/test_regress/t/t_gen_nonconst_bad.out +++ b/test_regress/t/t_gen_nonconst_bad.out @@ -2,9 +2,7 @@ 8 | nfound nfound(); | ^~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_gen_nonconst_bad.v:8:4: This may be because there's no search path specified with -I. - 8 | nfound nfound(); - | ^~~~~~ + ... This may be because there's no search path specified with -I. ... Looked in: nfound nfound.v diff --git a/test_regress/t/t_interface_mismodport_bad.out b/test_regress/t/t_interface_mismodport_bad.out index 0eaeffb14..a8b307c1f 100644 --- a/test_regress/t/t_interface_mismodport_bad.out +++ b/test_regress/t/t_interface_mismodport_bad.out @@ -1,6 +1,6 @@ %Error: t/t_interface_mismodport_bad.v:32:12: Can't find definition of 'bad' in dotted signal: 'isub.bad' 32 | isub.bad = i_value; | ^~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. ... Known scopes under 'bad': + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to From fe4ad7de648d67eafe153b69d6db48aa37c4eddc Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 May 2025 18:06:33 -0400 Subject: [PATCH 055/211] Internals: Use magic string for warnMore so lock-free. --- src/V3Error.cpp | 23 ++++++++++++++--------- src/V3Error.h | 4 ++-- src/V3FileLine.cpp | 8 ++++---- src/V3String.cpp | 10 ++++++++++ src/V3String.h | 2 ++ 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/V3Error.cpp b/src/V3Error.cpp index dcb00df3d..87f355915 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -113,7 +113,9 @@ void V3ErrorGuarded::vlAbortOrExit() VL_REQUIRES(m_mutex) { } } -string V3ErrorGuarded::warnMore() VL_REQUIRES(m_mutex) { return string(msgPrefix().size(), ' '); } +string V3ErrorGuarded::warnMoreSpaces() VL_REQUIRES(m_mutex) { + return string(msgPrefix().size(), ' '); +} void V3ErrorGuarded::suppressThisWarning() VL_REQUIRES(m_mutex) { #ifndef V3ERROR_NO_GLOBAL_ @@ -155,6 +157,8 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) // Suppress duplicate messages if (!m_messages.insert(firstLine).second) return; + msg = VString::replaceSubstr(msg, V3Error::warnMore(), warnMoreSpaces()); + string msg_additional; { string::size_type pos; @@ -172,7 +176,8 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) msg_additional.erase(pos + 1, 1); } if (!extra.empty() && !m_errorSuppressed) { - const string extraMsg = warnMore() + extra + "\n"; + string extraMsg = VString::replaceSubstr(extra, V3Error::warnMore(), warnMoreSpaces()); + extraMsg = warnMoreSpaces() + extraMsg + "\n"; const size_t pos = msg.find('\n'); msg.insert(pos + 1, extraMsg); } @@ -193,7 +198,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) && !m_describedEachWarn[m_errorCode]) { m_describedEachWarn[m_errorCode] = true; if (m_errorCode >= V3ErrorCode::EC_FIRST_NAMED) { - std::cerr << warnMore() << "... For " << (anError ? "error" : "warning") + std::cerr << warnMoreSpaces() << "... For " << (anError ? "error" : "warning") << " description see " << m_errorCode.url() << endl; } else if (m_errCount >= 1 && (m_errorCode == V3ErrorCode::EC_FATAL @@ -201,24 +206,24 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) || m_errorCode == V3ErrorCode::EC_FATALSRC) && !m_tellInternal) { m_tellInternal = true; - std::cerr << warnMore() + std::cerr << warnMoreSpaces() << "... This fatal error may be caused by the earlier error(s);" " resolve those first." << endl; } else if (!m_tellManual) { m_tellManual = true; - std::cerr << warnMore() << "... See the manual at " << m_errorCode.url() + std::cerr << warnMoreSpaces() << "... See the manual at " << m_errorCode.url() << " for more assistance." << endl; } if (!m_pretendError[m_errorCode] && !m_errorCode.hardError()) { - std::cerr << warnMore() << "... Use \"/* verilator lint_off " + std::cerr << warnMoreSpaces() << "... Use \"/* verilator lint_off " << m_errorCode.ascii() << " */\" and lint_on around source to disable this message." << endl; if (m_errorCode.dangerous()) { - std::cerr << warnMore() << "*** See " << m_errorCode.url() + std::cerr << warnMoreSpaces() << "*** See " << m_errorCode.url() << " before disabling this,\n"; - std::cerr << warnMore() << "else you may end up with different sim results." - << endl; + std::cerr << warnMoreSpaces() + << "else you may end up with different sim results." << endl; } } } diff --git a/src/V3Error.h b/src/V3Error.h index 8a5d669c1..5989b6ffc 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -349,7 +349,7 @@ public: V3RecursiveMutex m_mutex; // Make sure only single thread is in class string msgPrefix() VL_REQUIRES(m_mutex); // returns %Error/%Warn - string warnMore() VL_REQUIRES(m_mutex); + string warnMoreSpaces() VL_REQUIRES(m_mutex); void execErrorExitCb() VL_REQUIRES(m_mutex) { if (m_errorExitCb) m_errorExitCb(); } @@ -503,7 +503,7 @@ public: } // When printing an error/warning, print prefix for multiline message - static string warnMore() VL_REQUIRES(s().m_mutex) { return s().warnMore(); } + static string warnMore() VL_MT_SAFE { return "__WARNMORE__"; } // This function marks place in error message from which point message // should be printed after information on the error code. // The post-processing is done in v3errorEnd function. diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 2247695c7..94509087c 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -452,16 +452,16 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) { if (lastLineno()) { - return V3Error::s().warnMore() + string(ascii().size(), ' ') + ": "; + return V3Error::warnMore() + string(ascii().size(), ' ') + ": "; } else { - return V3Error::s().warnMore(); + return V3Error::warnMore(); } } string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { if (lastLineno()) { - return V3Error::s().warnMore() + ascii() + ": "; + return V3Error::warnMore() + ascii() + ": "; } else { - return V3Error::s().warnMore(); + return V3Error::warnMore(); } }; diff --git a/src/V3String.cpp b/src/V3String.cpp index 05405037b..bb80bb1d1 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -271,6 +271,16 @@ double VString::parseDouble(const string& str, bool* successp) { return d; } +string VString::replaceSubstr(const string& str, const string& from, const string& to) { + string result = str; + const size_t len = from.size(); + UASSERT_STATIC(len > 0, "Cannot replace empty string"); + for (size_t pos = 0; (pos = result.find(from, pos)) != string::npos; pos += len) { + result.replace(pos, len, to); + } + return result; +} + string VString::replaceWord(const string& str, const string& from, const string& to) { string result = str; const size_t len = from.size(); diff --git a/src/V3String.h b/src/V3String.h index 1c70ec96e..aa59107c6 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -126,6 +126,8 @@ public: static string::size_type leadingWhitespaceCount(const string& str); // Return double by parsing string static double parseDouble(const string& str, bool* successp); + // Replace substring. Often replaceWord is more appropriate. + static string replaceSubstr(const string& str, const string& from, const string& to); // Replace all occurrences of the word 'from' in 'str' with 'to'. A word is considered // to be a consecutive sequence of the characters [a-zA-Z0-9_]. Sub-words are not replaced. // e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple" From 295fae0edca38a36d8031a7d3188a32396772b1b Mon Sep 17 00:00:00 2001 From: Dominick Grochowina Date: Sun, 11 May 2025 08:01:13 -0400 Subject: [PATCH 056/211] Fix nullptr segfault in VerilatedVcd::emitTimeChange() (#5980) --- docs/CONTRIBUTORS | 1 + include/verilated_vcd_c.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 28cce9e65..28f3c1c1f 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -45,6 +45,7 @@ David Stanford David Turner Dercury Diego Roux +Dominick Grochowina Don Williamson Drew Ranck Drew Taussig diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 6bc7896b6..4b6588522 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -193,7 +193,7 @@ void VerilatedVcd::emitTimeChange(uint64_t timeui) { // timestamp backup and overwrite it. // This is faster then checking on every signal change if time needs to // be emitted. Note buffer flushes may still emit a rare duplicate. - if (m_wrTimeEndp == m_writep) m_writep = m_wrTimeBeginp; + if (m_wrTimeBeginp && m_wrTimeEndp == m_writep) m_writep = m_wrTimeBeginp; m_wrTimeBeginp = m_writep; { printStr("#"); From 0162e15b6e28d81cefc573b79ec048d369b6d2ca Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 May 2025 08:24:11 -0400 Subject: [PATCH 057/211] Tests: Rename property tests --- .../t/t_assert_property_pexpr_unsup.out | 113 ------------------ .../t/t_assert_recursive_property_unsup.out | 6 - .../t/{t_assert_property.py => t_property.py} | 0 .../t/{t_assert_property.v => t_property.v} | 0 ...roperty_fail_1.py => t_property_fail_1.py} | 2 +- ...fail_2_bad.py => t_property_fail_2_bad.py} | 2 +- ..._named_property.py => t_property_named.py} | 0 ...rt_named_property.v => t_property_named.v} | 0 ...perty_untyped.py => t_property_negated.py} | 0 ...egated_property.v => t_property_negated.v} | 0 test_regress/t/t_property_pexpr_unsup.out | 113 ++++++++++++++++++ ...xpr_unsup.py => t_property_pexpr_unsup.py} | 0 ...pexpr_unsup.v => t_property_pexpr_unsup.v} | 0 test_regress/t/t_property_recursive_unsup.out | 6 + ...unsup.py => t_property_recursive_unsup.py} | 0 ...y_unsup.v => t_property_recursive_unsup.v} | 0 ...ated_property.py => t_property_untyped.py} | 0 ...roperty_untyped.v => t_property_untyped.v} | 0 ...unsup.out => t_property_untyped_unsup.out} | 2 +- ...y_unsup.py => t_property_untyped_unsup.py} | 0 ...ped_unsup.v => t_property_untyped_unsup.v} | 0 ...var_unsup.out => t_property_var_unsup.out} | 8 +- ...y_var_unsup.py => t_property_var_unsup.py} | 0 ...rty_var_unsup.v => t_property_var_unsup.v} | 0 ...cked_port.py => t_public_unpacked_port.py} | 0 ...packed_port.v => t_public_unpacked_port.v} | 0 26 files changed, 126 insertions(+), 126 deletions(-) delete mode 100644 test_regress/t/t_assert_property_pexpr_unsup.out delete mode 100644 test_regress/t/t_assert_recursive_property_unsup.out rename test_regress/t/{t_assert_property.py => t_property.py} (100%) rename test_regress/t/{t_assert_property.v => t_property.v} (100%) rename test_regress/t/{t_assert_property_fail_1.py => t_property_fail_1.py} (93%) rename test_regress/t/{t_assert_property_fail_2_bad.py => t_property_fail_2_bad.py} (93%) rename test_regress/t/{t_assert_named_property.py => t_property_named.py} (100%) rename test_regress/t/{t_assert_named_property.v => t_property_named.v} (100%) rename test_regress/t/{t_assert_property_untyped.py => t_property_negated.py} (100%) rename test_regress/t/{t_negated_property.v => t_property_negated.v} (100%) create mode 100644 test_regress/t/t_property_pexpr_unsup.out rename test_regress/t/{t_assert_property_pexpr_unsup.py => t_property_pexpr_unsup.py} (100%) rename test_regress/t/{t_assert_property_pexpr_unsup.v => t_property_pexpr_unsup.v} (100%) create mode 100644 test_regress/t/t_property_recursive_unsup.out rename test_regress/t/{t_assert_property_untyped_unsup.py => t_property_recursive_unsup.py} (100%) rename test_regress/t/{t_assert_recursive_property_unsup.v => t_property_recursive_unsup.v} (100%) rename test_regress/t/{t_negated_property.py => t_property_untyped.py} (100%) rename test_regress/t/{t_assert_property_untyped.v => t_property_untyped.v} (100%) rename test_regress/t/{t_assert_property_untyped_unsup.out => t_property_untyped_unsup.out} (71%) rename test_regress/t/{t_assert_recursive_property_unsup.py => t_property_untyped_unsup.py} (100%) rename test_regress/t/{t_assert_property_untyped_unsup.v => t_property_untyped_unsup.v} (100%) rename test_regress/t/{t_assert_property_var_unsup.out => t_property_var_unsup.out} (54%) rename test_regress/t/{t_assert_property_var_unsup.py => t_property_var_unsup.py} (100%) rename test_regress/t/{t_assert_property_var_unsup.v => t_property_var_unsup.v} (100%) rename test_regress/t/{t_pub_unpacked_port.py => t_public_unpacked_port.py} (100%) rename test_regress/t/{t_pub_unpacked_port.v => t_public_unpacked_port.v} (100%) diff --git a/test_regress/t/t_assert_property_pexpr_unsup.out b/test_regress/t/t_assert_property_pexpr_unsup.out deleted file mode 100644 index 882db1882..000000000 --- a/test_regress/t/t_assert_property_pexpr_unsup.out +++ /dev/null @@ -1,113 +0,0 @@ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:25:13: Unsupported: strong (in property expression) - 25 | strong(a); - | ^ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:29:11: Unsupported: weak (in property expression) - 29 | weak(a); - | ^ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:33:9: Unsupported: until (in property expression) - 33 | a until b; - | ^~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:37:9: Unsupported: s_until (in property expression) - 37 | a s_until b; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:41:9: Unsupported: until_with (in property expression) - 41 | a until_with b; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:45:9: Unsupported: s_until_with (in property expression) - 45 | a s_until_with b; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:49:9: Unsupported: implies (in property expression) - 49 | a implies b; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:53:9: Unsupported: #-# (in property expression) - 53 | a #-# b; - | ^~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:57:9: Unsupported: #=# (in property expression) - 57 | a #=# b; - | ^~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:61:7: Unsupported: nexttime (in property expression) - 61 | nexttime a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:65:7: Unsupported: nexttime[] (in property expression) - 65 | nexttime [2] a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:69:7: Unsupported: s_nexttime (in property expression) - 69 | s_nexttime a; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:73:7: Unsupported: s_nexttime[] (in property expression) - 73 | s_nexttime [2] a; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:77:16: Unsupported: always (in property expression) - 77 | nexttime always a; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:77:7: Unsupported: nexttime (in property expression) - 77 | nexttime always a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:81:20: Unsupported: always (in property expression) - 81 | nexttime [2] always a; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:81:7: Unsupported: nexttime[] (in property expression) - 81 | nexttime [2] always a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:85:16: Unsupported: eventually (in property expression) - 85 | nexttime eventually a; - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:85:7: Unsupported: nexttime (in property expression) - 85 | nexttime eventually a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:89:20: Unsupported: always (in property expression) - 89 | nexttime [2] always a; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:89:7: Unsupported: nexttime[] (in property expression) - 89 | nexttime [2] always a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:93:16: Unsupported: s_eventually (in property expression) - 93 | nexttime s_eventually a; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:93:7: Unsupported: nexttime (in property expression) - 93 | nexttime s_eventually a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:35: Unsupported: always (in property expression) - 97 | nexttime s_eventually [2:$] always a; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:16: Unsupported: s_eventually[] (in property expression) - 97 | nexttime s_eventually [2:$] always a; - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:97:7: Unsupported: nexttime (in property expression) - 97 | nexttime s_eventually [2:$] always a; - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:101:17: Unsupported: accept_on (in property expression) - 101 | accept_on (a) b; - | ^ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:105:22: Unsupported: sync_accept_on (in property expression) - 105 | sync_accept_on (a) b; - | ^ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:109:17: Unsupported: reject_on (in property expression) - 109 | reject_on (a) b; - | ^ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:113:22: Unsupported: sync_reject_on (in property expression) - 113 | sync_reject_on (a) b; - | ^ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:117:9: Unsupported: iff (in property expression) - 117 | a iff b; - | ^~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:120:27: Unsupported: property argument data type - 120 | property p_arg_propery(property inprop); - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:123:27: Unsupported: sequence argument data type - 123 | property p_arg_seqence(sequence inseq); - | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:128:7: Unsupported: property case expression - 128 | case (a) endcase - | ^~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:131:7: Unsupported: property case expression - 131 | case (a) default: b; endcase - | ^~~~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:134:7: Unsupported: property case expression - 134 | if (a) b - | ^~ -%Error-UNSUPPORTED: t/t_assert_property_pexpr_unsup.v:137:7: Unsupported: property case expression - 137 | if (a) b else c - | ^~ -%Error: Exiting due to diff --git a/test_regress/t/t_assert_recursive_property_unsup.out b/test_regress/t/t_assert_recursive_property_unsup.out deleted file mode 100644 index 6176205df..000000000 --- a/test_regress/t/t_assert_recursive_property_unsup.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_assert_recursive_property_unsup.v:20:13: Unsupported: Recursive property call: 'check' - : ... note: In instance 't' - 20 | property check(int n); - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_assert_property.py b/test_regress/t/t_property.py similarity index 100% rename from test_regress/t/t_assert_property.py rename to test_regress/t/t_property.py diff --git a/test_regress/t/t_assert_property.v b/test_regress/t/t_property.v similarity index 100% rename from test_regress/t/t_assert_property.v rename to test_regress/t/t_property.v diff --git a/test_regress/t/t_assert_property_fail_1.py b/test_regress/t/t_property_fail_1.py similarity index 93% rename from test_regress/t/t_assert_property_fail_1.py rename to test_regress/t/t_property_fail_1.py index 64539f556..4caf548d3 100755 --- a/test_regress/t/t_assert_property_fail_1.py +++ b/test_regress/t/t_property_fail_1.py @@ -10,7 +10,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.top_filename = "t/t_assert_property.v" +test.top_filename = "t/t_property.v" test.compile(v_flags2=['+define+FAIL_ASSERT_1'], verilator_flags2=['--assert --cc']) diff --git a/test_regress/t/t_assert_property_fail_2_bad.py b/test_regress/t/t_property_fail_2_bad.py similarity index 93% rename from test_regress/t/t_assert_property_fail_2_bad.py rename to test_regress/t/t_property_fail_2_bad.py index f89198139..b0a2ba504 100755 --- a/test_regress/t/t_assert_property_fail_2_bad.py +++ b/test_regress/t/t_property_fail_2_bad.py @@ -10,7 +10,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.top_filename = "t/t_assert_property.v" +test.top_filename = "t/t_property.v" test.compile(v_flags2=['+define+FAIL_ASSERT_2'], verilator_flags2=['--assert --cc']) diff --git a/test_regress/t/t_assert_named_property.py b/test_regress/t/t_property_named.py similarity index 100% rename from test_regress/t/t_assert_named_property.py rename to test_regress/t/t_property_named.py diff --git a/test_regress/t/t_assert_named_property.v b/test_regress/t/t_property_named.v similarity index 100% rename from test_regress/t/t_assert_named_property.v rename to test_regress/t/t_property_named.v diff --git a/test_regress/t/t_assert_property_untyped.py b/test_regress/t/t_property_negated.py similarity index 100% rename from test_regress/t/t_assert_property_untyped.py rename to test_regress/t/t_property_negated.py diff --git a/test_regress/t/t_negated_property.v b/test_regress/t/t_property_negated.v similarity index 100% rename from test_regress/t/t_negated_property.v rename to test_regress/t/t_property_negated.v diff --git a/test_regress/t/t_property_pexpr_unsup.out b/test_regress/t/t_property_pexpr_unsup.out new file mode 100644 index 000000000..52541ca1f --- /dev/null +++ b/test_regress/t/t_property_pexpr_unsup.out @@ -0,0 +1,113 @@ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:25:13: Unsupported: strong (in property expression) + 25 | strong(a); + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:29:11: Unsupported: weak (in property expression) + 29 | weak(a); + | ^ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:33:9: Unsupported: until (in property expression) + 33 | a until b; + | ^~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:37:9: Unsupported: s_until (in property expression) + 37 | a s_until b; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:41:9: Unsupported: until_with (in property expression) + 41 | a until_with b; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:45:9: Unsupported: s_until_with (in property expression) + 45 | a s_until_with b; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:49:9: Unsupported: implies (in property expression) + 49 | a implies b; + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:53:9: Unsupported: #-# (in property expression) + 53 | a #-# b; + | ^~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:57:9: Unsupported: #=# (in property expression) + 57 | a #=# b; + | ^~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:61:7: Unsupported: nexttime (in property expression) + 61 | nexttime a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:65:7: Unsupported: nexttime[] (in property expression) + 65 | nexttime [2] a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:69:7: Unsupported: s_nexttime (in property expression) + 69 | s_nexttime a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:73:7: Unsupported: s_nexttime[] (in property expression) + 73 | s_nexttime [2] a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:77:16: Unsupported: always (in property expression) + 77 | nexttime always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:77:7: Unsupported: nexttime (in property expression) + 77 | nexttime always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:81:20: Unsupported: always (in property expression) + 81 | nexttime [2] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:81:7: Unsupported: nexttime[] (in property expression) + 81 | nexttime [2] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:85:16: Unsupported: eventually (in property expression) + 85 | nexttime eventually a; + | ^~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:85:7: Unsupported: nexttime (in property expression) + 85 | nexttime eventually a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:89:20: Unsupported: always (in property expression) + 89 | nexttime [2] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:89:7: Unsupported: nexttime[] (in property expression) + 89 | nexttime [2] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:93:16: Unsupported: s_eventually (in property expression) + 93 | nexttime s_eventually a; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:93:7: Unsupported: nexttime (in property expression) + 93 | nexttime s_eventually a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:97:35: Unsupported: always (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:97:16: Unsupported: s_eventually[] (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:97:7: Unsupported: nexttime (in property expression) + 97 | nexttime s_eventually [2:$] always a; + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:101:17: Unsupported: accept_on (in property expression) + 101 | accept_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:105:22: Unsupported: sync_accept_on (in property expression) + 105 | sync_accept_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:109:17: Unsupported: reject_on (in property expression) + 109 | reject_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:113:22: Unsupported: sync_reject_on (in property expression) + 113 | sync_reject_on (a) b; + | ^ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:9: Unsupported: iff (in property expression) + 117 | a iff b; + | ^~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:120:27: Unsupported: property argument data type + 120 | property p_arg_propery(property inprop); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:123:27: Unsupported: sequence argument data type + 123 | property p_arg_seqence(sequence inseq); + | ^~~~~~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:128:7: Unsupported: property case expression + 128 | case (a) endcase + | ^~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:131:7: Unsupported: property case expression + 131 | case (a) default: b; endcase + | ^~~~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:134:7: Unsupported: property case expression + 134 | if (a) b + | ^~ +%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:137:7: Unsupported: property case expression + 137 | if (a) b else c + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_assert_property_pexpr_unsup.py b/test_regress/t/t_property_pexpr_unsup.py similarity index 100% rename from test_regress/t/t_assert_property_pexpr_unsup.py rename to test_regress/t/t_property_pexpr_unsup.py diff --git a/test_regress/t/t_assert_property_pexpr_unsup.v b/test_regress/t/t_property_pexpr_unsup.v similarity index 100% rename from test_regress/t/t_assert_property_pexpr_unsup.v rename to test_regress/t/t_property_pexpr_unsup.v diff --git a/test_regress/t/t_property_recursive_unsup.out b/test_regress/t/t_property_recursive_unsup.out new file mode 100644 index 000000000..a55a38ac1 --- /dev/null +++ b/test_regress/t/t_property_recursive_unsup.out @@ -0,0 +1,6 @@ +%Error-UNSUPPORTED: t/t_property_recursive_unsup.v:20:13: Unsupported: Recursive property call: 'check' + : ... note: In instance 't' + 20 | property check(int n); + | ^~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_assert_property_untyped_unsup.py b/test_regress/t/t_property_recursive_unsup.py similarity index 100% rename from test_regress/t/t_assert_property_untyped_unsup.py rename to test_regress/t/t_property_recursive_unsup.py diff --git a/test_regress/t/t_assert_recursive_property_unsup.v b/test_regress/t/t_property_recursive_unsup.v similarity index 100% rename from test_regress/t/t_assert_recursive_property_unsup.v rename to test_regress/t/t_property_recursive_unsup.v diff --git a/test_regress/t/t_negated_property.py b/test_regress/t/t_property_untyped.py similarity index 100% rename from test_regress/t/t_negated_property.py rename to test_regress/t/t_property_untyped.py diff --git a/test_regress/t/t_assert_property_untyped.v b/test_regress/t/t_property_untyped.v similarity index 100% rename from test_regress/t/t_assert_property_untyped.v rename to test_regress/t/t_property_untyped.v diff --git a/test_regress/t/t_assert_property_untyped_unsup.out b/test_regress/t/t_property_untyped_unsup.out similarity index 71% rename from test_regress/t/t_assert_property_untyped_unsup.out rename to test_regress/t/t_property_untyped_unsup.out index e9985890c..5b663f222 100644 --- a/test_regress/t/t_assert_property_untyped_unsup.out +++ b/test_regress/t/t_property_untyped_unsup.out @@ -1,4 +1,4 @@ -%Error-UNSUPPORTED: t/t_assert_property_untyped_unsup.v:20:52: Untyped property port following a typed port +%Error-UNSUPPORTED: t/t_property_untyped_unsup.v:20:52: Untyped property port following a typed port 20 | property check(cyc_mod_2, logic [4:0] expected, arg3, untyped arg4, arg5); | ^~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest diff --git a/test_regress/t/t_assert_recursive_property_unsup.py b/test_regress/t/t_property_untyped_unsup.py similarity index 100% rename from test_regress/t/t_assert_recursive_property_unsup.py rename to test_regress/t/t_property_untyped_unsup.py diff --git a/test_regress/t/t_assert_property_untyped_unsup.v b/test_regress/t/t_property_untyped_unsup.v similarity index 100% rename from test_regress/t/t_assert_property_untyped_unsup.v rename to test_regress/t/t_property_untyped_unsup.v diff --git a/test_regress/t/t_assert_property_var_unsup.out b/test_regress/t/t_property_var_unsup.out similarity index 54% rename from test_regress/t/t_assert_property_var_unsup.out rename to test_regress/t/t_property_var_unsup.out index 8e61022c7..fb7fb50db 100644 --- a/test_regress/t/t_assert_property_var_unsup.out +++ b/test_regress/t/t_property_var_unsup.out @@ -1,15 +1,15 @@ -%Error-UNSUPPORTED: t/t_assert_property_var_unsup.v:17:11: Unsupported: property variable declaration +%Error-UNSUPPORTED: t/t_property_var_unsup.v:17:11: Unsupported: property variable declaration 17 | int prevcyc; | ^~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_assert_property_var_unsup.v:18:7: syntax error, unexpected '(', expecting endproperty +%Error: t/t_property_var_unsup.v:18:7: syntax error, unexpected '(', expecting endproperty 18 | (valid, prevcyc = cyc) |=> (cyc == prevcyc + 1); | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_assert_property_var_unsup.v:24:31: Unsupported: property variable default value +%Error-UNSUPPORTED: t/t_property_var_unsup.v:24:31: Unsupported: property variable default value 24 | property with_def(int nine = 9); | ^ -%Error: Internal Error: t/t_assert_property_var_unsup.v:7:8: ../V3ParseSym.h:#: Symbols suggest ending PROPERTY 'prop' but parser thinks ending MODULE 't' +%Error: Internal Error: t/t_property_var_unsup.v:7:8: ../V3ParseSym.h:#: Symbols suggest ending PROPERTY 'prop' but parser thinks ending MODULE 't' 7 | module t ( | ^ ... This fatal error may be caused by the earlier error(s); resolve those first. diff --git a/test_regress/t/t_assert_property_var_unsup.py b/test_regress/t/t_property_var_unsup.py similarity index 100% rename from test_regress/t/t_assert_property_var_unsup.py rename to test_regress/t/t_property_var_unsup.py diff --git a/test_regress/t/t_assert_property_var_unsup.v b/test_regress/t/t_property_var_unsup.v similarity index 100% rename from test_regress/t/t_assert_property_var_unsup.v rename to test_regress/t/t_property_var_unsup.v diff --git a/test_regress/t/t_pub_unpacked_port.py b/test_regress/t/t_public_unpacked_port.py similarity index 100% rename from test_regress/t/t_pub_unpacked_port.py rename to test_regress/t/t_public_unpacked_port.py diff --git a/test_regress/t/t_pub_unpacked_port.v b/test_regress/t/t_public_unpacked_port.v similarity index 100% rename from test_regress/t/t_pub_unpacked_port.v rename to test_regress/t/t_public_unpacked_port.v From aee5051526414af60ce50ad9d31985e405fdb80e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 May 2025 17:43:48 -0400 Subject: [PATCH 058/211] CI: Reduce action permissions per best practices --- .github/workflows/build.yml | 3 +++ .github/workflows/contributor.yml | 2 ++ .github/workflows/coverage.yml | 3 +++ .github/workflows/docker.yml | 3 +++ .github/workflows/format.yml | 2 ++ .github/workflows/msbuild.yml | 2 ++ .github/workflows/reusable-rtlmeter-run.yml | 2 +- .github/workflows/rtlmeter.yml | 9 +++++---- 8 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d90bc3241..f5d4e7978 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,9 @@ on: schedule: - cron: '0 0 * * 0' # weekly +permissions: + contents: read + defaults: run: shell: bash diff --git a/.github/workflows/contributor.yml b/.github/workflows/contributor.yml index 6485aaf48..64128c3b8 100644 --- a/.github/workflows/contributor.yml +++ b/.github/workflows/contributor.yml @@ -7,6 +7,8 @@ on: push: pull_request: workflow_dispatch: +permissions: + contents: read jobs: Test: name: "'docs/CONTRIBUTORS' was signed" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5fb56e65a..970b93efb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,6 +9,9 @@ on: schedule: - cron: '0 0 * * 0' # weekly +permissions: + contents: read + env: CI_OS_NAME: linux CI_COMMIT: ${{ github.sha }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index af7a21502..fa59cadb2 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,6 +22,9 @@ on: type: boolean default: false +permissions: + contents: write + jobs: build: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 4fbd884c5..ba0d6a892 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -7,6 +7,8 @@ on: push: pull_request_target: workflow_dispatch: +permissions: + contents: write jobs: format: runs-on: ubuntu-22.04 diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index b7e5677c6..4555e497d 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -10,6 +10,8 @@ on: workflow_dispatch: schedule: - cron: 0 0 * * 0 # weekly +permissions: + contents: read env: CI_OS_NAME: win CI_COMMIT: ${{ github.sha }} diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index 12f73a3de..e3d62fd3b 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -84,7 +84,7 @@ jobs: - name: Execute cases working-directory: rtlmeter - continue-on-error: true # Do not fail on error, so we can at leat save the successful results + continue-on-error: true # Do not fail on error, so we can at leat save the successful results run: | ./rtlmeter run --verbose --cases='${{ inputs.cases }}' --compileArgs='${{ inputs.compileArgs }}' --executeArgs='${{ inputs.executeArgs }}' # My YAML highlighter sucks, so I put this comment here wiht a phony closing quote mark to make it work: ' diff --git a/.github/workflows/rtlmeter.yml b/.github/workflows/rtlmeter.yml index cb2e43edf..549da1950 100644 --- a/.github/workflows/rtlmeter.yml +++ b/.github/workflows/rtlmeter.yml @@ -10,6 +10,9 @@ on: schedule: - cron: '0 2 * * *' # Daily, starting at 02:00 UTC +permissions: + contents: read + defaults: run: shell: bash @@ -115,10 +118,8 @@ jobs: combine-results: name: Combine results - needs: - - run-gcc - - run-clang - if: ${{ always() }} # Run even if dependencies failed + needs: [run-gcc, run-clang] + if: ${{ always() }} # Run even if dependencies failed runs-on: ubuntu-24.04 steps: - name: Download all GCC results From 8100bc64a0984efb14312c3443719ae1d5c4c7af Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 May 2025 22:36:16 -0400 Subject: [PATCH 059/211] Commentary --- bin/verilator | 6 ++-- docs/guide/exe_verilator.rst | 56 +++++++++++++++++++----------------- docs/guide/files.rst | 4 +-- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/bin/verilator b/bin/verilator index 89c18ea6f..777947dce 100755 --- a/bin/verilator +++ b/bin/verilator @@ -390,8 +390,8 @@ detailed descriptions of these arguments. --no-json-edit-nums Don't dump editNum in .tree.json files --no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json --json-only Create JSON parser output (.tree.json and .meta.json) - --json-only-meta-output .tree.meta.json output filename - --json-only-output .tree.json output filename + --json-only-meta-output Set .tree.meta.json output filename + --json-only-output Set .tree.json output filename --l2-name Verilog scope name of the top module --language Default language standard to parse -LDFLAGS Linker pre-object arguments for makefile @@ -448,7 +448,7 @@ detailed descriptions of these arguments. --quiet-exit Don't print the command on failure --quiet-stats Don't print statistics --relative-includes Resolve includes relative to current file - --reloop-limit Minimum iterations for forming loops + --reloop-limit Minimum iterations for forming loops --report-unoptflat Extra diagnostics for UNOPTFLAT --rr Run Verilator and record with rr --runtime-debug Enable model runtime debugging diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index ef956e3fd..debb506f8 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -446,9 +446,9 @@ Summary: .. option:: --dump-tree-dot - Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz - Dot format. This option implies :vlopt:`--dump-tree`, unless - :vlopt:`--dumpi-tree` was passed explicitly. + Rarely needed - for developer use. Enable dumping Ast .tree.dot debug + files in Graphviz Dot format. This option implies :vlopt:`--dump-tree`, + unless :vlopt:`--dumpi-tree` was passed explicitly. .. option:: --dump-tree-json @@ -568,9 +568,9 @@ Summary: .. option:: -fno-const-before-dfg - Do not apply any global expression folding prior to the DFG pass. This - option is solely for the purpose of DFG testing and should not be used - otherwise. + Rarely needed. Do not apply any global expression folding prior to the + DFG pass. This option is solely for the purpose of DFG testing and + should not be used otherwise. .. option:: -fno-const-bit-op-tree @@ -578,24 +578,25 @@ Summary: .. option:: -fno-dfg - Disable all use of the DFG-based combinational logic optimizer. - Alias for :vlopt:`-fno-dfg-pre-inline` and :vlopt:`-fno-dfg-post-inline`. + Rarely needed. Disable all use of the DFG-based combinational logic + optimizer. Alias for :vlopt:`-fno-dfg-pre-inline` and + :vlopt:`-fno-dfg-post-inline`. .. option:: -fno-dfg-peephole - Disable the DFG peephole optimizer. + Rarely needed. Disable the DFG peephole optimizer. .. option:: -fno-dfg-peephole- - Disable individual DFG peephole optimizer pattern. + Rarely needed. Disable individual DFG peephole optimizer pattern. .. option:: -fno-dfg-post-inline - Do not apply the DFG optimizer after inlining. + Rarely needed. Do not apply the DFG optimizer after inlining. .. option:: -fno-dfg-pre-inline - Do not apply the DFG optimizer before inlining. + Rarely needed. Do not apply the DFG optimizer before inlining. .. option:: -fno-expand @@ -647,8 +648,9 @@ Summary: .. option:: -fno-var-split - Do not attempt to split variables automatically. Variables explicitly - annotated with :option:`/*verilator&32;split_var*/` are still split. + Rarely needed. Do not attempt to split variables + automatically. Variables explicitly annotated with + :option:`/*verilator&32;split_var*/` are still split. .. option:: -future0